Compare commits

..
35 Commits
Author SHA1 Message Date
johan12345 d04dbc60f0 redirect to new website 2023-06-06 19:08:38 +02:00
johan12345 59326e6688 fix link 2023-05-28 22:34:18 +02:00
johan12345 b07488751b privacy: add Tesla realtime data source 2023-05-28 22:24:19 +02:00
Johan von Forstner 77440828bd add fronyx to privacy policy (#240) 2022-10-08 13:48:45 +02:00
johan12345 8aa353c048 add EnBW to privacy policy 2022-10-08 12:17:44 +02:00
Johan von Forstner c9ed60d2de Chargeprice FAQ: use   between number and unit 2022-07-13 15:08:41 +02:00
Johan von Forstner b9905ff3db Chargeprice FAQ: adjust wording
developers -> developer
2022-07-13 15:01:42 +02:00
Johan von Forstner 9b0c13ecd4 FAQ: add differences between F-Droid and Play Store 2022-07-13 14:59:51 +02:00
Johan von Forstner 9da68a54ba add page with list of permissions 2022-06-17 10:05:28 +02:00
johan12345 a827eb929b update Chargeprice FAQ 2022-05-26 18:35:26 +02:00
johan12345 8e37cce864 fix typos 2021-11-06 22:27:34 +01:00
johan12345 e56b983ee4 add link to Chargeprice FAQ 2021-11-06 22:26:46 +01:00
johan12345 0a59ccdde5 add Chargeprice FAQ 2021-11-06 22:24:25 +01:00
Johan von Forstner 1db166181c translate FAQ and privacy into English (fixes #110) 2021-09-26 15:50:12 +02:00
Johan von Forstner 667a688e54 FAQ regarding fault reports: add info about OpenChargeMap 2021-09-26 15:24:38 +02:00
Johan von Forstner b3bdc27034 move german FAQ and privacy pages to subdirectory 2021-09-26 15:15:16 +02:00
Johan von Forstner 8c3084e7ed FAQ: add charger icon with two lightning bolts 2021-09-26 15:09:19 +02:00
Johan von Forstner 8aef10fe8b FAQ: put verified icon in title 2021-09-23 18:42:43 +02:00
Johan von Forstner 70937e1632 FAQ: add chargers with two lightning icons 2021-09-23 18:42:09 +02:00
Johan von Forstner 7e0dd02fba improve description of FAQ 2021-09-23 18:37:44 +02:00
Johan von Forstner 7ab6fbe856 privacy.html: Add OpenChargeMap 2021-08-11 20:28:51 +02:00
Johan von Forstner c569bd95d9 Update faq.html 2021-04-18 14:38:47 +02:00
Johan von Forstner 8c393f5eee FAQ: fix typo 2021-04-18 14:22:33 +02:00
Johan von Forstner 7508046ad9 FAQ: add information about plug colors 2021-04-18 14:21:23 +02:00
Johan von Forstner 4ad8a12b2d privacy.html: Add Chargeprice 2021-04-17 23:13:41 +02:00
Johan von Forstner 938198fae0 FAQ: Add description for verified charger badge 2021-04-05 22:01:14 +02:00
Johan von Forstner f0998f6dc6 privacy.html: Include Mapbox 2021-03-24 08:48:46 +01:00
Johan von Forstner a92e2b6c74 Update faq.html
Remove outdated FAQ entry
2020-09-08 22:33:20 +02:00
Johan von Forstner ed3dfe0014 link 2020-07-03 18:45:19 +02:00
Johan von Forstner e96f07196f add FAQ about OpenStreetMap 2020-07-03 18:43:45 +02:00
Johan von Forstner c09ce5769f swap colors for >= 11 kW and < 11 kW
(similar to GE website and Wattfinder)
2020-06-17 21:39:27 +02:00
Johan von Forstner 6b83d1eff8 add FAQ page 2020-05-24 11:46:41 +02:00
Johan von Forstner 6c13507e0d privacy: add Google Maps 2020-05-17 13:28:51 +02:00
Johan von Forstner c0c43946c9 Create CNAME 2020-04-13 19:53:19 +02:00
Johan von Forstner 7713742770 add privacy policy 2020-04-13 19:51:37 +02:00
247 changed files with 191 additions and 19338 deletions

No files matched your search

-12
View File
@@ -1,12 +0,0 @@
*.iml
.gradle
/local.properties
/.idea/*
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
apikeys.xml
/app/release/app-release.aab
/_img/connectors/*.ai
-35
View File
@@ -1,35 +0,0 @@
language: android
dist: trusty
android:
components:
- build-tools-29.0.3
- android-29
env:
global:
- secure: KYdFlMarsyXw+OHht1Atp+Kirbw9O09Ck14EjFuKb1eNtknurZ/tGEXuD+8xWh1W8W21kgHEG7s3rzru53t29buz+FW9f+ZmhEWXFP3OydyvXLw4BAVVOjm6xG2uHX/8MOGLJNM7cfaF25EPQ+kznHe84R29KaLH90mNRr2lPa4VnfbcnvDStiVaez/vJ72UoYSP5HICAzoF70yC3ZvvCK1hZv71UIysCbFE2IkxvMhG9OOGebdnRmFssaRCrvfRLjitobcLzkPWzZZIqdjNASf8/iAxX8VgGBYfVj8ID06AfMrtgXNJRCvcD0LICraQ+WPUbikMunRieGO8PNHSB5vKdPoC50aLUa0RoRb4G3QM1pR2A8xAFlIJFX2R7iY+2t24L9hRFqB98+QoQzutfkAI1T0rzem/wtpZpuan+bDawDJEHGCeYbE0aPDAl6lytgrEE9fRgV3c1jJLQzu0xIWG8YLl3iMg0hL+c0wCKXoeqrfCFS6kYmmG7W+rQp4tCZifvRbWfAXwIPQieffKxqdEuUwiUsYxdzCu9v9uU3nflEOLLuRgeMP3gV8mpur9b5GztpkfgfzcAqsF+NiY01kYgGtrgCYlMy0TxASE+UuALrtkQtU01wwhs9RH7Az0Ib3C+MT5DTjxHQCYETIViocmNEG2vfAbgHazCpGAhcY=
- secure: Hoko50vP+Mwm/O4CWvPvjMxd1gGhi+Bultjyy1WpludSmZFCfKz45Mj1EqzeYk6MeMLvGOEkSLB5wjdXdgJ9j5gEOF5K34k4vESJA7+DqDO3I7Xw9cnWgOXdFqB0qGHar0TVP3Dfg7ZcRgtmeX6t2uoELFLiS9GvTnbZXk4PCUybd979Xi8XHjEQV7+3EZbSjtsI4GFeIK1rrjd0I+UM88zYrWnz1KhdCjWvQb4iZjo+ib6NmGGEMqR7jJPRZz3KB01Y+n5h21qq9/Nv31zQIqt2B5nRxy3vBvvqKapgprIk+hVOpNnBU8w89uWUU6tYUeFk0t7z1TYWjgaBrMmGCM+aKkQ2q5F/ygNzDwB+KkpJx709Yhf0ZX8g2yvkdz+Ok7moYuvmrOPOf4E/U3BlfZxbGtRD2bGYbDgHLFYlTn6v5J2kJHDJAz31yvF5jvJejDaPp2IBVfoMRy7ZJFmGUNHGd9Se6bRwxS++AoobP5WDrBiUXNe2KKDMs3e7vbaO+hLbZ9XHpjeWIJGUfvtTee8EHZqF/8A3ju53V4/R0ehlOv2UZbpYNqcmwrsy9/R4pgMfDkG3Q054LmYrxD8DIC9b8excVMwWRP5aQ1TqZnxO2B1/vJU87RcnGl3jekeHTdHXbRq9BMV4dAdPfB9X3nGIi2GgV0iTBk+24xccOc0=
- secure: RsN/2nBVv0byMzwchuAhDti1AWKECg3Uzqk6Ahgaqg02zx8GHj60j0qNax7KuMUg70X3G4b3m8ZzndAU0wcJ98UyIku7ofUYgXHm0XYKTJwiyyhrKJ8pZ4qoeCoRkitIGIitlb84fSufVamcoLyLNbLUsO5OzL+Uscyhq/BwAhyOhj5FB9DM+GE9ntanQ4m3k4guMyMctR9CGS6Tk4LKJ1WCm0AnjTPalc+we+7yORY2J/9d6VHLKfaFXbpuWmrdnFfDZqxqcUODsxPVE0LuUtXyKQDTjjfQc8106Z/z19AJS+oLm/2UND84PD3MqsjX6EX0/9k3fY0OsoCuQAivDRmtevQ0bDQrTAyeBLcfnPCw/MYiJWyBcaYBAYK42EAfsFTDBRIduFB/Dpvg+8GuLZSdm4xVYpTlQ2pMtlGNWIGIRQsjk9LZ8swq8QBMiiF/bpMGKdfmnyQj8jkEOCWaAzkR9O+4E4Y7PuBENef9XuV0hLMryCrML2YXigxAKkEUOPhfnG+AbEY+g2kAMp+2EtXaG3tsGxoMhEYA+uRd3o2cacQMMwRpnePH5DYg6F05mrvdHPpt+P9UR1iHaHmjBPAYeksmrdP8bS088zcnePgiL6+6N0m3+l8Krihmxg5RyWjnH18IwX4RO+xg4x3cW+zaScCDbbotDMEYtChF+Hs=
before_install:
- openssl aes-256-cbc -K $encrypted_53968681344a_key -iv $encrypted_53968681344a_iv -in _ci/keystore.jks.enc -out _ci/keystore.jks -d
script:
- "./gradlew lintFossDebug testFossDebugUnitTest lintGoogleDebug testGoogleDebugUnitTest"
- "./gradlew assembleRelease"
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- "$HOME/.gradle/caches/"
- "$HOME/.gradle/wrapper/"
- "$HOME/.android/build-cache"
deploy:
provider: releases
api_key:
secure: B+V5Fz8k9HbpecyMjpJuLr8aVBrdwtDBDkQh4YQ8nu+Da4AiYwEJZseWXhOWs+oms0gNen9bBxsakQQKu7GKYDs8gIXZZtANWsc0gse8xo+cYT7NqEM3jP6mM3ytAv7VNRX3N2cdL7xazELK3/5+mghfORAAdXXYKUFGG5eTKoML8zgdPVN8E9QFqiusLXqoKhxOMCSE4NS+Di7CGlUmnidRTWg6yxhE085zljmYv2owS0NRbr5a4/zW6Z9xZPALGAqsOvIvpZHuOC2s0eMJWMmYGkK/Ws/LAVxfj4U+YkFp9hlZC0zEg/JoS19Gf57QmEu+vsoQ3uOBYBFv9NPI/R9kVH6o0hcOxId3J0u+ewSGWuceGLRpizXuMxKIvLTS5j6GWkxdSieWjwh/OuVB+ciAHNM31B7GP4FWnfz0ZaEVxI/tPenNipZdl9oXdyyBQQ00vPlYp0jT80XhaMh5rDwWMUPaEjRafvymcNyqZ0iVOr0rq1CbdT92STMSmA1U3/rmhtCMD5IGD0b+gQl+VpPKe1QXViYftVxCGL+s4ke4DUZD7HR20fGs8zu61Elnwci1HufbetKFL5TmxoKSLkWFSkzrtBaJnEruZIxhNUMkUL2UPynaOcPNzLoumjHXrUb3m3s0yE4OFelmJ6mJfXswP38sS8kj3wB7R/gC4rw=
file:
- app/build/outputs/apk/foss/release/app-foss-release.apk
- app/build/outputs/apk/google/release/app-google-release.apk
on:
repo: johan12345/EVMap
tags: true
skip_cleanup: 'true'
+1
View File
@@ -0,0 +1 @@
evmap.vonforst.net
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020 Johan von Forstner
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.
-48
View File
@@ -1,48 +0,0 @@
EVMap [![Build Status](https://travis-ci.org/johan12345/EVMap.svg?branch=master)](https://travis-ci.org/johan12345/EVMap)
=====
<img src="https://raw.githubusercontent.com/johan12345/EVMap/master/_img/appicon_cropped.svg?sanitize=true" width=80 alt="Logo"/>
Android app to access the goingelectric.de electric vehicle charging station directory.
<a href="https://play.google.com/store/apps/details?id=net.vonforst.evmap" target="_blank">
<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" alt="Get it on Google Play" height="100"/></a>
Features
--------
- [Material Design](https://material.io/)
- Shows all charging stations from the community-maintained [GoingElectric.de](https://www.goingelectric.de/stromtankstellen/) directory
- Realtime availability information (beta)
- Search places
- Favorites list, also with availability information
- No ads, fully open source
- Compatible with Android 5.0 and above
Screenshots
-----------
<img src="https://raw.githubusercontent.com/johan12345/EVMap/master/_img/screenshots/phone/01_main.png" width=250 alt="Screenshot 1"/><img src="https://raw.githubusercontent.com/johan12345/EVMap/master/_img/screenshots/phone/02_detail.png" width=250 alt="Screenshot 2"/>
Development setup
-----------------
The App is developed using Android Studio.
For testing the app, you need to obtain API Keys for the
[GoingElectric API](https://www.goingelectric.de/stromtankstellen/api/)
as well as for [Google APIs](https://console.developers.google.com/)
("Maps SDK for Android" and "Places API" need to be activated). These APIs need to be put into the
app in the form of a resource file called `apikeys.xml` under `app/src/main/res/values`, with the
following content:
```xml
<resources>
<string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">
insert your Google Maps key here
</string>
<string name="goingelectric_key" translatable="false">
insert your GoingElectric key here
</string>
</resources>
```
Binary file not shown.
-74
View File
@@ -1,74 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 192 192"
style="enable-background:new 0 0 192 192;" xml:space="preserve">
<style type="text/css">
.st0{display:none;}
.st1{display:inline;}
.st2{display:inline;opacity:0.2;clip-path:url(#SVGID_2_);fill:none;stroke:#212121;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:10;}
.st3{display:inline;opacity:0.2;clip-path:url(#SVGID_2_);fill:none;stroke:#FFFFFF;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:10;}
.st4{opacity:0.2;clip-path:url(#SVGID_2_);fill:none;stroke:#212121;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st5{opacity:0.2;clip-path:url(#SVGID_2_);fill:none;stroke:#FFFFFF;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st6{display:none;fill:#802C27;}
.st7{fill:#FFB300;}
.st8{fill:#90A4AE;}
.st9{fill:#546E7A;}
.st10{fill:#00E676;}
.st11{fill:#FFFFFF;fill-opacity:0.2;}
.st12{fill:#3E2723;fill-opacity:0.2;}
.st13{opacity:0.45;}
</style>
<g class="st0">
<defs>
<path id="SVGID_1_" d="M122.5,57.1c-5.8-7.7-15.2-12.4-24.7-12.9H94c-8.1,0.5-15.9,3.9-21.7,9.6c-4.3,4.3-7.4,9.7-8.6,15.7
c-1.5,7.5-0.1,15.4,3.5,22.1c2.3,4.5,5.2,8.6,8.1,12.8c4.9,6.8,9.8,13.7,13.2,21.3c3.1,6.5,4.9,13.6,5.6,20.8
c0.4,0.6,1.1,1,1.7,1.5c2.6-1,2-4,2.5-6.2c1.9-12.8,8.7-24.2,16.1-34.6c6.6-9.2,14.1-19,14.4-30.8V74
C128.5,67.9,126.3,61.9,122.5,57.1z M106.2,74.3l-12.2,21V79.5h-5.2V60.3h17.5l-7,14H106.2z" />
</defs>
<clipPath id="SVGID_2_" class="st1">
<use xlink:href="#SVGID_1_" style="overflow:visible;" />
</clipPath>
<path class="st2" d="M106.2,74.3h-7" />
<path class="st2" d="M106.2,60.3c0,0-17.5,0-17.5,0" />
<line class="st3" x1="93.9" y1="79.5" x2="88.7" y2="79.5" />
<path class="st4" d="M94,79v16.2" />
<line class="st5" x1="106.2" y1="60.3" x2="99.2" y2="74.3" />
<line class="st5" x1="106.2" y1="74.3" x2="93.9" y2="95.2" />
</g>
<path class="st6" d="M255-187.1L255-187.1L234.2-208l-5.9,5.9l11.8,11.8c-5.3,2-9,7.1-9,13.1c0,7.7,6.3,14,14,14
c2,0,3.9-0.4,5.6-1.2v40.4c0,3.1-2.5,5.6-5.6,5.6s-5.6-2.5-5.6-5.6v-25.2c0-6.2-5-11.2-11.2-11.2h-5.6v-39.2c0-6.2-5-11.2-11.2-11.2
h-33.7c-6.2,0-11.2,5-11.2,11.2v89.7h56.1V-152h8.4v28c0,7.7,6.3,14,14,14s14-6.3,14-14v-53.2C259.1-181.1,257.6-184.6,255-187.1
M245.1-171.6c-3.1,0-5.6-2.5-5.6-5.6c0-3.1,2.5-5.6,5.6-5.6s5.6,2.5,5.6,5.6C250.7-174.1,248.2-171.6,245.1-171.6 M189.1-126.8
V-152h-11.3l22.4-42.1v28h11.2L189.1-126.8z" />
<g>
<g>
<path class="st7"
d="M67.6,120.6L65.7,104l-2.9,0.3l1.9,16.6L67.6,120.6z M77.9,119.4l-1.9-16.6l-2.9,0.3l1.9,16.6L77.9,119.4z" />
<path class="st8" d="M83.3,142c-0.9,1.1-1.6,1.8-1.7,1.9c-2.6,2.1-4.7,2.7-6.4,1.9c-3-1.5-2.8-7.1-2.7-7.7l2.1,0.1
c-0.1,1.6,0.2,5,1.6,5.7c0.8,0.4,2.2-0.1,4-1.6l0,0c0,0,5.8-5.8,4.6-10.4c-1.4-5.5,5-13.4,7.1-16.1l0.3-0.3l1.7,1.3l-0.3,0.4
c-6.5,8-7.2,12.1-6.7,14.2C87.9,135.4,85.2,139.7,83.3,142z" />
<path class="st8" d="M61.2,120.4l0.8,6.8l6.3,4.2l8.5-0.9l5.2-5.5l-0.8-6.8L61.2,120.4z" />
<g>
<path class="st9" d="M76.7,130.5l-8.5,0.9l1.8,7.5l6.7-0.8L76.7,130.5L76.7,130.5z M82.8,112.5l0.7,6.2l-24.4,2.8l-0.7-6.2
L82.8,112.5z" />
</g>
</g>
<g>
<g>
<path class="st10" d="M101.9,44.1c-17.5,0-31.7,14.2-31.7,31.7c0,23.9,26.7,36.4,29.9,70.5c0.1,1,0.9,1.7,1.9,1.7
s1.8-0.7,1.9-1.7c3.2-34.1,29.9-46.6,29.9-70.5C133.6,58.2,119.4,44.1,101.9,44.1z" />
<path class="st11" d="M101.9,44.8c17.4,0,31.5,14,31.7,31.3c0-0.1,0-0.2,0-0.3c0-17.5-14.2-31.7-31.7-31.7S70.2,58.2,70.2,75.8
c0,0.1,0,0.2,0,0.3C70.4,58.8,84.5,44.8,101.9,44.8L101.9,44.8z" />
<path class="st12" d="M103.8,145.5c-0.1,1-0.9,1.7-1.9,1.7s-1.8-0.7-1.9-1.7c-3.1-34-29.6-46.5-29.8-70.1c0,0.2,0,0.3,0,0.5
c0,23.9,26.7,36.4,29.9,70.5c0.1,1,0.9,1.7,1.9,1.7s1.8-0.7,1.9-1.7c3.2-34.1,29.9-46.6,29.9-70.5c0-0.2,0-0.3,0-0.5
C133.4,99,106.9,111.5,103.8,145.5L103.8,145.5z" />
</g>
<path class="st13"
d="M94.6,60.3v19.2h5.2v15.7l12.2-21h-7l7-14C112.1,60.3,94.6,60.3,94.6,60.3z" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.2 KiB

-40
View File
@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
viewBox="0 0 75.4 104" style="enable-background:new 0 0 75.4 104;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFB300;}
.st1{fill:#90A4AE;}
.st2{fill:#546E7A;}
.st3{fill:#00E676;}
.st4{fill:#FFFFFF;fill-opacity:0.2;}
.st5{fill:#3E2723;fill-opacity:0.2;}
.st6{opacity:0.45;enable-background:new ;}
</style>
<g>
<g>
<path class="st0"
d="M9.2,76.5L7.3,59.9l-2.9,0.3l1.9,16.6L9.2,76.5z M19.5,75.3l-1.9-16.6L14.7,59l1.9,16.6L19.5,75.3z" />
<path class="st1" d="M24.9,97.9c-0.9,1.1-1.6,1.8-1.7,1.9c-2.6,2.1-4.7,2.7-6.4,1.9c-3-1.5-2.8-7.1-2.7-7.7l2.1,0.1
c-0.1,1.6,0.2,5,1.6,5.7c0.8,0.4,2.2-0.1,4-1.6l0,0c0,0,5.8-5.8,4.6-10.4c-1.4-5.5,5-13.4,7.1-16.1l0.3-0.3l1.7,1.3l-0.3,0.4
c-6.5,8-7.2,12.1-6.7,14.2C29.5,91.3,26.8,95.6,24.9,97.9z" />
<path class="st1" d="M2.8,76.3l0.8,6.8l6.3,4.2l8.5-0.9l5.2-5.5l-0.8-6.8L2.8,76.3z" />
<g>
<path class="st2"
d="M18.3,86.4l-8.5,0.9l1.8,7.5l6.7-0.8V86.4L18.3,86.4z M24.4,68.4l0.7,6.2L0.7,77.4L0,71.2L24.4,68.4z" />
</g>
</g>
<g>
<g>
<path class="st3" d="M43.5,0C26,0,11.8,14.2,11.8,31.7c0,23.9,26.7,36.4,29.9,70.5c0.1,1,0.9,1.7,1.9,1.7s1.8-0.7,1.9-1.7
c3.2-34.1,29.9-46.6,29.9-70.5C75.2,14.1,61,0,43.5,0z" />
<path class="st4" d="M43.5,0.7c17.4,0,31.5,14,31.7,31.3c0-0.1,0-0.2,0-0.3C75.2,14.2,61,0,43.5,0S11.8,14.1,11.8,31.7
c0,0.1,0,0.2,0,0.3C12,14.7,26.1,0.7,43.5,0.7L43.5,0.7z" />
<path class="st5" d="M45.4,101.4c-0.1,1-0.9,1.7-1.9,1.7s-1.8-0.7-1.9-1.7c-3.1-34-29.6-46.5-29.8-70.1c0,0.2,0,0.3,0,0.5
c0,23.9,26.7,36.4,29.9,70.5c0.1,1,0.9,1.7,1.9,1.7s1.8-0.7,1.9-1.7c3.2-34.1,29.9-46.6,29.9-70.5c0-0.2,0-0.3,0-0.5
C75,54.9,48.5,67.4,45.4,101.4L45.4,101.4z" />
</g>
<path class="st6"
d="M36.2,16.2v19.2h5.2v15.7l12.2-21h-7l7-14C53.7,16.2,36.2,16.2,36.2,16.2z" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

-429
View File
@@ -1,429 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
viewBox="0 0 360 200" style="enable-background:new 0 0 360 200;" xml:space="preserve">
<style type="text/css">
.st0{fill:#A5A4AA;}
.st1{fill:#FFFFFF;}
.st2{fill:#454B55;}
.st3{fill:#7CA5C7;}
.st4{fill:#7CA4C5;}
.st5{fill:#86CBC4;}
.st6{fill:#039F96;}
.st7{fill:#3F4B55;}
.st8{fill:#444C55;}
.st9{fill:#E5E5E4;}
.st10{fill:#98BED8;}
.st11{fill:#EC6678;}
.st12{fill:#D95C6C;}
.st13{fill:#E9EEF4;}
.st14{fill:#687078;}
.st15{fill:#EF7879;}
.st16{opacity:0.2;fill:#020203;enable-background:new ;}
.st17{fill:#C1D777;}
.st18{fill:#C7C7C6;}
.st19{fill:#93CEBF;}
.st20{fill:#FFDE78;}
.st21{fill:#EDA975;}
.st22{fill:#F5EAA1;}
</style>
<g>
<path class="st0" d="M47.7,59.7l3,3l-1.9,1.9l7.6,7.6l3.7-3.7l-7.6-7.6l-1.8,1.8" />
<path class="st1"
d="M51.1,130.2L13.4,92.5c-2.1-2.1-2.1-5.4,0-7.5L32,66.3c2-2,5.4-2,7.5,0L77.2,104" />
<path class="st1" d="M46.3,85.9L32.9,99.3c-1,1-2.7,1-3.7,0l-8.7-8.7c-1-1-1-2.7,0-3.7l13.4-13.4c1-1,2.7-1,3.7,0l8.7,8.7
C47.4,83.2,47.3,84.9,46.3,85.9z" />
<path class="st2" d="M78.5,105.3c-0.7,0.7-1.9,0.7-2.6,0L38.2,67.7c-1.3-1.3-3.6-1.4-4.9,0L14.7,86.3c-1.3,1.3-1.3,3.5,0,4.9
l37.7,37.7c0.7,0.7,0.7,1.9,0,2.6c-0.7,0.7-1.9,0.7-2.6,0L12.1,93.8c-2.8-2.8-2.8-7.3,0-10.1L30.7,65c2.8-2.8,7.3-2.8,10.1,0
l37.7,37.7C79.2,103.5,79.2,104.6,78.5,105.3z" />
<path class="st3" d="M46.3,85.9L32.9,99.3c-1,1-2.7,1-3.7,0l-8.7-8.7c-1-1-1-2.7,0-3.7l13.4-13.4c1-1,2.7-1,3.7,0l8.7,8.7
C47.4,83.2,47.3,84.9,46.3,85.9z" />
<path class="st2" d="M47.6,87.2l-13.4,13.4c-1.7,1.7-4.6,1.7-6.3,0l-8.7-8.7c-1.7-1.7-1.7-4.6,0-6.3l13.4-13.4
c1.7-1.7,4.6-1.7,6.3,0l8.7,8.7C49.4,82.6,49.4,85.4,47.6,87.2z M21.8,88.2c-0.3,0.3-0.3,0.8,0,1.1l8.7,8.7c0.3,0.3,0.8,0.3,1.1,0
L45,84.6c0.3-0.3,0.3-0.8,0-1.1l-8.7-8.7c-0.3-0.3-0.8-0.3-1.1,0L21.8,88.2z M82.2,101.6l-33.5,33.5c-0.7,0.7-1.9,0.7-2.6,0
c-0.7-0.7-0.7-1.9,0-2.6L79.6,99c0.7-0.7,1.9-0.7,2.6,0S82.9,100.9,82.2,101.6z M80.3,95.9c-1.7,1.7-4.6,1.7-6.3,0L58.9,80.9
c-0.3-0.3-0.8-0.3-1.1,0l-1.9,1.9c-0.7,0.7-1.9,0.7-2.6,0c-0.7-0.7-0.7-1.9,0-2.6l1.9-1.9c1.7-1.7,4.6-1.7,6.3,0l15.1,15.1
c0.3,0.3,0.8,0.3,1.1,0c0.3-0.3,0.3-0.8,0-1.1L57,71.5c-0.7-0.7-0.7-1.9,0-2.6s1.9-0.7,2.6,0l20.7,20.7C82,91.3,82,94.2,80.3,95.9z
" />
<path class="st2" d="M61.4,69.7l-3.7,3.7c-0.7,0.7-1.9,0.7-2.6,0l-7.6-7.6c-0.7-0.7-0.7-1.9,0-2.6l0.6-0.6L46.4,61
c-0.7-0.7-0.7-1.9,0-2.6s1.9-0.7,2.6,0l1.7,1.7l0.5-0.5c0.7-0.7,1.9-0.7,2.6,0l7.5,7.5C62,67.8,62.1,69,61.4,69.7z M56.4,69.5
l1.1-1.1l-4.9-4.9l-1.1,1.1L56.4,69.5z" />
<g>
<path class="st4" d="M57.6,114.1c-0.2,0.2-0.5,0.4-0.9,0.5c-1,0.2-2-0.4-2.2-1.3l-1.3-5.7l-1.3,1.9c-0.4,0.6-1.1,0.9-1.8,0.7
c-0.7-0.2-1.3-0.7-1.4-1.4l-2.3-9.5c-0.2-1,0.4-2,1.3-2.2c1-0.2,2,0.4,2.2,1.3l1.3,5.7l1.3-1.9c0.4-0.6,1.1-0.9,1.8-0.7
c0.7,0.2,1.3,0.7,1.4,1.4l2.2,9.5C58.2,113.1,58,113.7,57.6,114.1z" />
</g>
</g>
<g>
<path class="st1" d="M29.9,146.1l24,24c1,1,1,2.5,0,3.5l-14.1,14.1c-1,1-2.5,1-3.5,0l-24-24c-1-1-1-2.5,0-3.5l14.1-14.1
C27.4,145.1,28.9,145.1,29.9,146.1z M11.5,152.4l7.1-7.1l4.2,4.2l-7.1,7.1L11.5,152.4z" />
<path class="st5" d="M37,153.1l17,17c1,1,1,2.5,0,3.5l-14.1,14.1c-1,1-2.5,1-3.5,0l-17-17c-1-1-1-2.5,0-3.5l14.1-14.1
C34.4,152.1,36,152.1,37,153.1z" />
<path class="st6" d="M50.4,170.1L50.4,170.1c1-1,1-2.5,0-3.5L37,153.1c-1-1-2.5-1-3.5,0l-1.8,1.8l15.2,15.2
C47.9,171.1,49.4,171.1,50.4,170.1z" />
<path class="st7" d="M55,174.7l-14.1,14.1c-1.6,1.6-4.1,1.6-5.7,0l-24-24c-1.6-1.6-1.6-4.1,0-5.7L25.3,145c1.6-1.6,4.1-1.6,5.7,0
l24,24C56.6,170.6,56.6,173.1,55,174.7z M13.3,161.3c-0.4,0.4-0.4,1,0,1.4l24,24c0.4,0.4,1,0.4,1.4,0l14.1-14.1
c0.4-0.4,0.4-1,0-1.4l-24-24c-0.4-0.4-1-0.4-1.4,0L13.3,161.3z" />
<path class="st1" d="M39.1,183.5c-0.6,0.6-1.5,0.6-2.1,0l-8.8-8.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l8.8,8.8
C39.7,182,39.7,182.9,39.1,183.5z M25,169.4c-0.3,0.3-0.7,0.5-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4c-0.3-0.3-0.5-0.7-0.4-1.1
c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0.1-0.3s0.1-0.2,0.1-0.3c0.3-0.4,0.7-0.7,1.2-0.7c0.4,0,0.8,0.1,1.1,0.4c0.1,0.1,0.1,0.1,0.2,0.2
c0.1,0.1,0.1,0.2,0.1,0.3c0,0.1,0.1,0.2,0.1,0.3c0,0.1,0,0.2,0,0.3C25.4,168.7,25.2,169.1,25,169.4z" />
<path class="st7" d="M23.9,150.6c-0.6,0.6-1.5,0.6-2.1,0l-3.2-3.2l-4.9,4.9l0.4,0.4c0.6,0.6,0.6,1.5,0,2.1s-1.5,0.6-2.1,0l-1.4-1.4
c-0.6-0.6-0.6-1.5,0-2.1l7.1-7.1c0.6-0.6,1.5-0.6,2.1,0l4.2,4.2C24.5,149.1,24.5,150,23.9,150.6z" />
</g>
<g>
<path class="st1" d="M117.2,186.9l-1.1,1.1l-35.4-35.4l25.8-25.8c1.9-1.9,5.1-1.9,7.1,0l24.7,24.7l0,0
C137.2,161.8,124.5,179.6,117.2,186.9z" />
<path class="st5" d="M103.2,148.4l16.9-4.1c0.6-0.1,0.9,0.6,0.4,0.9l-10,6.6l3.4,3.4c0.3,0.3,0.1,0.8-0.3,0.8l-15.4,2.4
c-0.5,0.1-0.8-0.6-0.4-0.9l8.3-5.1l-3.3-3.3C102.8,148.9,102.9,148.5,103.2,148.4z" />
<path class="st8" d="M131.7,166.8l-5.3-5.3c-2.5-2.5-2.5-6.6,0-9.2l2.8-2.8c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1l-2.8,2.8
c-1.4,1.4-1.4,3.6,0,4.9l5.3,5.3c0.6,0.6,0.6,1.5,0,2.1S132.2,167.3,131.7,166.8z" />
<path class="st8" d="M115.1,189l-35.4-35.4c-0.6-0.6-0.6-1.5,0-2.1l25.8-25.8c2.5-2.5,6.6-2.5,9.2,0l24.7,24.7c0,0,0,0,0,0l2.8,2.8
c0.6,0.6,0.6,1.5,0,2.1s-1.5,0.6-2.1,0l-0.8-0.8c-2.9,11-14.6,26.8-21.1,33.3l-1.1,1.1C116.6,189.6,115.6,189.6,115.1,189z
M82.9,152.6l33.2,33.2c7.6-7.6,19.2-24.3,20.7-33.8l-24.2-24.2c-1.4-1.4-3.6-1.4-4.9,0C107.6,127.9,82.9,152.6,82.9,152.6z" />
<path class="st8" d="M115.1,189l-36.8-36.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l36.8,36.8c0.6,0.6,0.6,1.5,0,2.1
C116.6,189.6,115.6,189.6,115.1,189z" />
<path class="st1" d="M88.3,156.3c-1.9-1.9-4.9-1.9-6.8,0s-1.9,4.9,0,6.8c0,0,0,0,0,0c1.9,1.9,4.9,1.9,6.8,0S90.2,158.1,88.3,156.3
C88.3,156.3,88.3,156.3,88.3,156.3z M110.9,178.9c-1.9-1.9-4.9-1.9-6.8,0c-1.9,1.9-1.9,4.9,0,6.8c0,0,0,0,0,0
c1.9,1.9,4.9,1.9,6.8,0C112.8,183.8,112.8,180.8,110.9,178.9C110.9,178.9,110.9,178.9,110.9,178.9z" />
<path class="st8" d="M80.4,164.1c-2.5-2.5-2.4-6.5,0-8.9c2.4-2.4,6.5-2.4,8.9,0c2.4,2.4,2.4,6.5,0,8.9
C86.9,166.6,82.9,166.6,80.4,164.1z M87.2,157.3c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0
C88.5,160.7,88.5,158.6,87.2,157.3z" />
<g>
<path class="st8" d="M103.1,186.7c-2.5-2.5-2.4-6.5,0-8.9s6.5-2.4,8.9,0c2.5,2.5,2.4,6.5,0,8.9
C109.5,189.2,105.5,189.2,103.1,186.7z M109.9,180c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0
S111.2,181.3,109.9,180z" />
</g>
<g>
<path class="st8" d="M103.1,186.7L73.4,157c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l29.7,29.7c0.6,0.6,0.6,1.5,0,2.1
C104.6,187.3,103.7,187.3,103.1,186.7z" />
</g>
<g>
<path class="st8" d="M68.4,152.1c-0.1-0.1-0.1-0.1-0.2-0.2s-0.1-0.2-0.1-0.3s-0.1-0.2-0.1-0.3c0-0.1,0-0.2,0-0.3
c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0.1-0.3c0.1,0,0.1-0.1,0.2-0.2s0.1-0.2,0.2-0.2s0.1-0.1,0.2-0.2c0.1-0.1,0.2-0.1,0.3-0.1
s0.2,0,0.3-0.1s0.2,0,0.3,0c0.1,0,0.2,0,0.3,0c0.1,0,0.2,0,0.3,0.1c0.1,0,0.2,0.1,0.3,0.1c0.2,0.1,0.3,0.2,0.4,0.4
c0.1,0.1,0.1,0.2,0.1,0.3s0.1,0.2,0.1,0.3c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2-0.1,0.3s-0.1,0.2-0.1,0.3
c-0.1,0.1-0.1,0.2-0.2,0.2s-0.1,0.1-0.2,0.2c-0.1,0.1-0.2,0.1-0.3,0.1c-0.1,0-0.2,0.1-0.3,0.1c-0.1,0-0.2,0-0.3,0
c-0.1,0-0.2,0-0.3,0s-0.2,0-0.3-0.1c-0.1,0-0.2-0.1-0.3-0.1C68.6,152.3,68.5,152.2,68.4,152.1z" />
</g>
</g>
<g>
<path class="st1" d="M336.4,33.8l-13.6,13.6c-1.9,1.9-5.1,1.9-7.1,0l-13.8-13.8c-1.9-1.9-1.9-5.1,0-7.1L315.5,13
c1.9-1.9,5.1-1.9,7.1,0l13.8,13.8C338.3,28.7,338.3,31.9,336.4,33.8z" />
<path class="st2" d="M337.4,34.9l-13.6,13.6c-2.5,2.5-6.6,2.5-9.2,0l-13.8-13.8c-2.5-2.5-2.5-6.6,0-9.2l13.6-13.6
c2.5-2.5,6.6-2.5,9.2,0l13.8,13.8C340,28.2,340,32.3,337.4,34.9z M303,27.6c-1.4,1.4-1.4,3.6,0,4.9l13.8,13.8
c1.4,1.4,3.6,1.4,4.9,0l13.6-13.6c1.4-1.4,1.4-3.6,0-4.9L321.5,14c-1.4-1.4-3.6-1.4-4.9,0L303,27.6z" />
<path class="st9" d="M325.4,36.4L325.4,36.4c-3.4,3.4-9,3.4-12.4,0l-0.1-0.1c-3.4-3.4-3.4-9,0-12.4l0,0c3.4-3.4,9-3.4,12.4,0
l0.1,0.1C328.8,27.5,328.8,33,325.4,36.4z" />
<path class="st2" d="M326.5,37.5c-4,4-10.5,4-14.5,0l-0.1-0.1c-4-4-4-10.5,0-14.5s10.5-4,14.5,0l0.1,0.1
C330.5,27,330.5,33.5,326.5,37.5z M314,25c-2.8,2.8-2.8,7.4,0,10.3l0.1,0.1c2.8,2.8,7.4,2.8,10.3,0c2.8-2.8,2.8-7.4,0-10.3
l-0.1-0.1C321.4,22.2,316.8,22.2,314,25z" />
<path class="st2" d="M315.3,31.5c-0.7,0.7-0.7,1.8,0,2.5c0.7,0.7,1.8,0.7,2.5,0c0,0,0,0,0,0c0.7-0.7,0.7-1.8,0-2.5
C317.2,30.8,316,30.8,315.3,31.5C315.3,31.5,315.3,31.5,315.3,31.5z M320.4,26.4c-0.7,0.7-0.7,1.8,0,2.5s1.8,0.7,2.5,0c0,0,0,0,0,0
c0.7-0.7,0.7-1.8,0-2.5C322.3,25.7,321.1,25.7,320.4,26.4C320.4,26.4,320.4,26.4,320.4,26.4z" />
<path class="st1"
d="M341.7,43.3l3.8,3.8c2.6,2.6,2.6,6.8,0,9.4l0,0c-2.6,2.6-6.8,2.6-9.4,0l-3.8-3.8L341.7,43.3z" />
<path class="st2" d="M346.6,57.6c-3.2,3.2-8.3,3.2-11.5,0l-3.1-3.1c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l3.1,3.1
c2,2,5.3,2,7.3,0c2-2,2-5.3,0-7.3l-3.1-3.1c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l3.1,3.1
C349.8,49.3,349.8,54.4,346.6,57.6z" />
<path class="st2" d="M335.1,51.8c-0.6,0.6-1.5,0.6-2.1,0l-3.3-3.3c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l3.3,3.3
C335.7,50.3,335.7,51.2,335.1,51.8z M340.8,46.2c-0.6,0.6-1.5,0.6-2.1,0l-3.3-3.3c-0.6-0.6-0.6-1.5,0-2.1s1.5-0.6,2.1,0l3.3,3.3
C341.4,44.6,341.4,45.6,340.8,46.2z" />
<path class="st2" d="M344.1,43.1l-12,12c-0.6,0.6-1.5,0.6-2.1,0s-0.6-1.5,0-2.1l12-12c0.6-0.6,1.5-0.6,2.1,0S344.7,42.5,344.1,43.1
z M349.6,60.6c-0.6,0.6-1.5,0.6-2.1,0l-3-3c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l3,3C350.1,59,350.2,60,349.6,60.6z" />
<g>
<path class="st10" d="M339.8,50.8c-0.5,0.5-0.5,1.4,0,1.9c0.5,0.5,1.4,0.5,1.9,0c0,0,0,0,0,0c0.5-0.5,0.5-1.4,0-1.9
C341.2,50.3,340.3,50.3,339.8,50.8C339.8,50.8,339.8,50.8,339.8,50.8z" />
</g>
</g>
<g>
<path class="st11" d="M171,149l34.3,34.3l3.8-3.8c2.1-2.1,2.1-5.5,0-7.6l-13.6-13.6l-0.6-0.2c-2.3-0.8-3-3.7-1.3-5.5l-1.2-1.2
C186.3,145.2,179.1,145.5,171,149" />
<path class="st12" d="M182,146.6C181.9,146.6,181.9,146.6,182,146.6c-0.4-0.1-0.7-0.1-1-0.1c-3.1,0.1-6.4,1-9.9,2.4l0,0l10.9,10.9
C185.6,156.3,185.6,150.3,182,146.6 M210.7,175.6c-3.6-2.7-8.8-2.4-12.2,0.9l6.8,6.8l3.8-3.8C210.2,178.4,210.7,177,210.7,175.6" />
<path class="st2" d="M204.3,184.4L170,150c-0.4-0.4-0.5-0.8-0.4-1.3c0.1-0.5,0.4-0.9,0.9-1.1c9.8-4.2,17.1-3.3,23.1,2.8l0.1,0.1
c0.6,0.6,0.6,1.5,0,2.1c-0.6,0.6-1.5,0.6-2.1,0l-0.1-0.1c-4.7-4.7-10.3-5.7-17.8-2.9l31.6,31.6l2.8-2.8c1.5-1.5,1.5-4,0-5.5
l-12.1-12.1c-0.4-0.4-0.5-0.8-0.4-1.3l0.6-3.8c0.1-0.8,0.9-1.4,1.7-1.2c0.8,0.1,1.4,0.9,1.2,1.7l-0.5,3l11.6,11.6
c2.7,2.7,2.7,7.1,0,9.8l-3.8,3.8C205.8,185,204.9,184.9,204.3,184.4z" />
<path class="st1" d="M179.1,149.4c-2.1-2.1-5.5-2.1-7.6,0c-2.1,2.1-2.1,5.5,0,7.6c0,0,0,0,0,0c2.1,2.1,5.5,2.1,7.6,0
C181.3,155,181.3,151.6,179.1,149.4C179.1,149.4,179.1,149.4,179.1,149.4z M198.9,164.9c-1.2-1.2-3.1-1.2-4.2,0s-1.2,3.1,0,4.2
s3.1,1.2,4.2,0C200.1,168,200.1,166.1,198.9,164.9z" />
<path class="st2" d="M170.4,158.1c-2.7-2.7-2.7-7.1,0-9.8c2.7-2.7,7.1-2.7,9.8,0c2.7,2.7,2.7,7.1,0,9.8
C177.5,160.8,173.1,160.8,170.4,158.1z M178.1,150.5c-1.5-1.5-4-1.5-5.5,0s-1.5,4,0,5.5s4,1.5,5.5,0
C179.6,154.5,179.6,152,178.1,150.5z" />
<path class="st1" d="M209,179.3c-2.1-2.1-5.5-2.1-7.6,0c-2.1,2.1-2.1,5.5,0,7.6c0,0,0,0,0,0c2.1,2.1,5.5,2.1,7.6,0
C211.1,184.8,211.1,181.4,209,179.3C209,179.3,209,179.3,209,179.3z" />
<path class="st2" d="M200.3,188c-2.7-2.7-2.7-7.1,0-9.8c2.7-2.7,7.1-2.7,9.8,0c2.7,2.7,2.7,7.1,0,9.8
C207.4,190.7,203,190.7,200.3,188z M207.9,180.3c-1.5-1.5-4-1.5-5.5,0s-1.5,4,0,5.5c1.5,1.5,4,1.5,5.5,0
C209.4,184.3,209.4,181.9,207.9,180.3z" />
<path class="st2" d="M200.3,188l-34.5-34.5c-0.6-0.6-0.6-1.5,0-2.1s1.5-0.6,2.1,0l34.5,34.5c0.6,0.6,0.6,1.5,0,2.1
C201.8,188.6,200.9,188.6,200.3,188z M161.6,149.3c-0.3-0.3-0.5-0.7-0.4-1.1c0-0.4,0.1-0.8,0.4-1.1c0.3-0.3,0.7-0.5,1.1-0.4
c0.8,0,1.5,0.7,1.5,1.5c0,0.4-0.1,0.8-0.4,1.1c-0.3,0.3-0.7,0.5-1.1,0.4C162.2,149.7,161.9,149.6,161.6,149.3z" />
</g>
<g>
<path class="st1"
d="M107.9,26.1L95.6,13.7c-2.7-2.7-7-2.9-9.9-0.6l-9,7.1l24.7,24.7l7.1-8.9C110.9,33,110.6,28.7,107.9,26.1z" />
<path class="st13"
d="M107.9,26.1l-6.2-6.2L89,32.6l12.4,12.4l7.1-8.9C110.9,33,110.6,28.7,107.9,26.1z" />
<path class="st14" d="M78.7,18.1l24.7,24.7l-12,12L66.7,30.1L78.7,18.1z" />
<path class="st8" d="M91.1,30.4l12.4,12.4l-12,12L79.1,42.5L91.1,30.4z" />
<path class="st14" d="M76.6,20.2L76.6,20.2c-1.9-1.9-5.1-1.9-7.1,0l-6.4,6.4l7.1,7.1l6.4-6.4C78.6,25.3,78.6,22.1,76.6,20.2z
M95,58.4l6.4-6.4c1.9-1.9,1.9-5.1,0-7.1l0,0c-1.9-1.9-5.1-1.9-7.1,0l-6.4,6.4" />
<path class="st8" d="M69.2,34.7c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4c1.4-1.4,1.4-3.6,0-4.9c-1.4-1.4-3.6-1.4-4.9,0l-6.4,6.4
c-0.6,0.6-1.5,0.6-2.1,0c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4c2.5-2.5,6.6-2.5,9.2,0s2.5,6.6,0,9.2l-6.4,6.4
C70.7,35.3,69.8,35.3,69.2,34.7z M93.9,59.4c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4c1.4-1.4,1.4-3.6,0-4.9s-3.6-1.4-4.9,0L89,52.4
c-0.6,0.6-1.5,0.6-2.1,0c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4c2.5-2.5,6.6-2.5,9.2,0c2.5,2.5,2.5,6.6,0,9.2l-6.4,6.4
C95.5,60,94.5,60,93.9,59.4z" />
<path class="st8" d="M101.8,43.8c0,0-0.1-0.1-0.1-0.1C95,34.6,87,26.6,78,19.9c-0.6-0.5-0.8-1.4-0.3-2.1c0.5-0.6,1.4-0.8,2.1-0.3
c9.3,6.9,17.5,15.1,24.4,24.4c0.5,0.6,0.4,1.6-0.3,2.1C103.2,44.3,102.4,44.3,101.8,43.8z" />
<path class="st8" d="M75.6,21.2c0,0-0.1-0.1-0.1-0.1c-0.5-0.6-0.4-1.6,0.2-2.1l9-7.1c3.6-2.8,8.7-2.5,12,0.7L109,25
c3.3,3.3,3.5,8.4,0.7,12l-7.1,8.9c-0.5,0.6-1.4,0.7-2.1,0.2c-0.7-0.5-0.7-1.4-0.2-2.1l7.1-8.9c1.9-2.4,1.7-5.8-0.4-8L94.5,14.7
c-2.2-2.2-5.6-2.4-8-0.5l-9,7.1C76.9,21.9,76.1,21.8,75.6,21.2z M92.9,56.3l-4.9,4.9l-2.8-2.8l4.9-4.9 M68.1,31.5l-4.9,4.9
l-2.8-2.8l4.9-4.9" />
<path class="st8" d="M86.9,62.3L84,59.4c-0.6-0.6-0.6-1.5,0-2.1l4.9-4.9c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1l-3.9,3.9
l0.7,0.7l3.9-3.9c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1L89,62.3C88.4,62.9,87.5,62.9,86.9,62.3z M62.1,37.5l-2.8-2.8
c-0.6-0.6-0.6-1.5,0-2.1l4.9-4.9c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1l-3.9,3.9l0.7,0.7l3.9-3.9c0.6-0.6,1.5-0.6,2.1,0
c0.6,0.6,0.6,1.5,0,2.1l-4.9,4.9C63.6,38.1,62.7,38.1,62.1,37.5z" />
<path class="st8" d="M95,60.5L61.1,26.6c-0.6-0.6-0.6-1.5,0-2.1s1.5-0.6,2.1,0l33.9,33.9c0.6,0.6,0.6,1.5,0,2.1
C96.5,61.1,95.6,61.1,95,60.5z" />
<path class="st1" d="M84.2,43.3l-5.9-5.9c-0.6-0.6-0.6-1.5,0-2.1l3.5-3.5c2.2-2.2,5.8-2.2,8.1,0l0,0c2.2,2.2,2.2,5.8,0,8.1
l-3.5,3.5C85.7,43.9,84.8,43.9,84.2,43.3z" />
<path class="st8" d="M106.3,22.3l-1.1,1.1l-7.1-7.1l1.1-1.1l0,0c1.2-1.2,3.1-1.2,4.2,0l2.8,2.8C107.5,19.2,107.5,21.1,106.3,22.3
L106.3,22.3z" />
<path class="st1" d="M74.9,22c-1-1-2.6-1-3.5,0c-1,1-1,2.6,0,3.5c1,1,2.6,1,3.5,0C75.8,24.5,75.8,22.9,74.9,22z M99.6,46.7
c-1-1-2.6-1-3.5,0s-1,2.6,0,3.5s2.6,1,3.5,0S100.6,47.7,99.6,46.7z" />
<path class="st8" d="M89,64.4L59.4,34.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l29.6,29.6c0.6,0.6,0.6,1.5,0,2.1
C90.5,65,89.6,65,89,64.4z M96.4,67.6c-0.6-0.6-1.5-0.6-2.1,0c-0.6,0.6-0.6,1.5,0,2.1s1.5,0.6,2.1,0C97,69.1,97,68.2,96.4,67.6z" />
</g>
<g>
<path class="st1" d="M139.1,66.4l24,24c1,1,1,2.5,0,3.5l-14.1,14.1c-1,1-2.5,1-3.5,0l-24-24c-1-1-1-2.5,0-3.5l14.1-14.1
C136.5,65.4,138.1,65.4,139.1,66.4z M120.7,72.7l7.1-7.1l4.2,4.2l-7.1,7.1L120.7,72.7z" />
<path class="st5" d="M146.1,73.4l17,17c1,1,1,2.5,0,3.5l-14.1,14.1c-1,1-2.5,1-3.5,0l-17-17c-1-1-1-2.5,0-3.5l14.1-14.1
C143.6,72.4,145.1,72.4,146.1,73.4z" />
<path class="st6" d="M159.6,90.4L159.6,90.4c1-1,1-2.5,0-3.5l-13.4-13.4c-1-1-2.5-1-3.5,0l-1.8,1.8L156,90.4
C157,91.4,158.6,91.4,159.6,90.4z" />
<path class="st7" d="M164.2,95L150,109.1c-1.6,1.6-4.1,1.6-5.7,0l-24-24c-1.6-1.6-1.6-4.1,0-5.7l14.1-14.1c1.6-1.6,4.1-1.6,5.7,0
l24,24C165.7,90.9,165.7,93.4,164.2,95z M122.4,81.6c-0.4,0.4-0.4,1,0,1.4l24,24c0.4,0.4,1,0.4,1.4,0L162,92.9c0.4-0.4,0.4-1,0-1.4
l-24-24c-0.4-0.4-1-0.4-1.4,0L122.4,81.6z" />
<path class="st1" d="M148.2,103.8c-0.6,0.6-1.5,0.6-2.1,0l-8.8-8.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l8.8,8.8
C148.8,102.3,148.8,103.2,148.2,103.8z M134.1,89.7c-0.3,0.3-0.7,0.5-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4c-0.3-0.3-0.5-0.7-0.4-1.1
c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0.1-0.3s0.1-0.2,0.1-0.3c0.3-0.4,0.7-0.7,1.2-0.7c0.4,0,0.8,0.1,1.1,0.4c0.1,0.1,0.1,0.1,0.2,0.2
c0.1,0.1,0.1,0.2,0.1,0.3s0.1,0.2,0.1,0.3c0,0.1,0,0.2,0,0.3C134.6,89,134.4,89.4,134.1,89.7z" />
<path class="st7" d="M133,71c-0.6,0.6-1.5,0.6-2.1,0l-3.2-3.2l-4.9,4.9l0.4,0.4c0.6,0.6,0.6,1.5,0,2.1c-0.6,0.6-1.5,0.6-2.1,0
l-1.4-1.4c-0.6-0.6-0.6-1.5,0-2.1l7.1-7.1c0.6-0.6,1.5-0.6,2.1,0l4.2,4.2C133.6,69.4,133.6,70.4,133,71z" />
</g>
<g>
<path class="st0" d="M315,113.9l3,3l-1.9,1.9l7.6,7.6l3.7-3.7l-7.6-7.6l-1.8,1.8" />
<path class="st1"
d="M318.3,184.5l-37.7-37.7c-2.1-2.1-2.1-5.4,0-7.5l18.7-18.7c2-2,5.4-2,7.5,0l37.7,37.7" />
<path class="st1" d="M313.6,140.2l-13.4,13.4c-1,1-2.7,1-3.7,0l-8.7-8.7c-1-1-1-2.7,0-3.7l13.4-13.4c1-1,2.7-1,3.7,0l8.7,8.7
C314.6,137.5,314.6,139.2,313.6,140.2z" />
<path class="st2" d="M345.7,159.6c-0.7,0.7-1.9,0.7-2.6,0L305.5,122c-1.3-1.3-3.6-1.4-4.9,0l-18.7,18.7c-1.3,1.3-1.3,3.5,0,4.9
l37.7,37.7c0.7,0.7,0.7,1.9,0,2.6c-0.7,0.7-1.9,0.7-2.6,0l-37.7-37.7c-2.8-2.8-2.8-7.3,0-10.1l18.7-18.7c2.8-2.8,7.3-2.8,10.1,0
l37.7,37.7C346.5,157.8,346.4,158.9,345.7,159.6z" />
<path class="st3" d="M313.6,140.2l-13.4,13.4c-1,1-2.7,1-3.7,0l-8.7-8.7c-1-1-1-2.7,0-3.7l13.4-13.4c1-1,2.7-1,3.7,0l8.7,8.7
C314.6,137.5,314.6,139.2,313.6,140.2z" />
<path class="st2" d="M314.9,141.5l-13.4,13.4c-1.7,1.7-4.6,1.7-6.3,0l-8.7-8.7c-1.7-1.7-1.7-4.6,0-6.3l13.4-13.4
c1.7-1.7,4.6-1.7,6.3,0l8.7,8.7C316.6,136.9,316.6,139.7,314.9,141.5z M289.1,142.5c-0.3,0.3-0.3,0.8,0,1.1l8.7,8.7
c0.3,0.3,0.8,0.3,1.1,0l13.4-13.4c0.3-0.3,0.3-0.8,0-1.1l-8.7-8.7c-0.3-0.3-0.8-0.3-1.1,0L289.1,142.5z M349.5,155.9l-33.5,33.5
c-0.7,0.7-1.9,0.7-2.6,0c-0.7-0.7-0.7-1.9,0-2.6l33.5-33.5c0.7-0.7,1.9-0.7,2.6,0C350.2,154,350.2,155.2,349.5,155.9z M347.5,150.2
c-1.7,1.7-4.6,1.7-6.3,0l-15.1-15.1c-0.3-0.3-0.8-0.3-1.1,0l-1.9,1.9c-0.7,0.7-1.9,0.7-2.6,0c-0.7-0.7-0.7-1.9,0-2.6l1.9-1.9
c1.7-1.7,4.6-1.7,6.3,0l15.1,15.1c0.3,0.3,0.8,0.3,1.1,0c0.3-0.3,0.3-0.8,0-1.1l-20.7-20.7c-0.7-0.7-0.7-1.9,0-2.6s1.9-0.7,2.6,0
l20.7,20.7C349.2,145.6,349.2,148.5,347.5,150.2z" />
<path class="st2" d="M328.6,124l-3.7,3.7c-0.7,0.7-1.9,0.7-2.6,0l-7.6-7.6c-0.7-0.7-0.7-1.9,0-2.6l0.6-0.6l-1.7-1.7
c-0.7-0.7-0.7-1.9,0-2.6c0.7-0.7,1.9-0.7,2.6,0l1.7,1.7l0.5-0.5c0.7-0.7,1.9-0.7,2.6,0l7.5,7.5C329.3,122.1,329.3,123.3,328.6,124z
M323.6,123.8l1.1-1.1l-4.9-4.9l-1.1,1.1L323.6,123.8z" />
<g>
<path class="st4" d="M324.8,168.4c-0.2,0.2-0.5,0.4-0.9,0.5c-1,0.2-2-0.4-2.2-1.3l-1.3-5.7l-1.3,1.9c-0.4,0.6-1.1,0.9-1.8,0.7
c-0.7-0.2-1.3-0.7-1.4-1.4l-2.3-9.5c-0.2-1,0.4-2,1.3-2.2c1-0.2,2,0.4,2.2,1.3l1.3,5.7l1.3-1.9c0.4-0.6,1.1-0.9,1.8-0.7
c0.7,0.2,1.3,0.7,1.4,1.4l2.2,9.5C325.4,167.4,325.3,168,324.8,168.4z" />
</g>
</g>
<g>
<path class="st15"
d="M233.8,171.2l15.3,15.3l15.9-26.2c0.3-0.5,0.3-1.1-0.2-1.6l-3.3-3.3c-0.4-0.4-1-0.5-1.6-0.2L233.8,171.2z" />
<path class="st1"
d="M244.5,164.7l-6.2,3.8l13.5,13.5l0,0l3.8-6.2L244.5,164.7z M256.1,157.6l-6.2,3.8l9,9l3.8-6.2L256.1,157.6z" />
<path class="st16"
d="M263.6,157.4l1.3,1.3c0.4,0.4,0.5,1,0.2,1.6l-16,26.2l-3.5-3.5l18.2-24C264.1,158.4,264,157.8,263.6,157.4z" />
<g>
<path class="st2" d="M248.4,187.2l-15.3-15.3c-0.2-0.2-0.3-0.5-0.3-0.8c0-0.3,0.2-0.6,0.5-0.7l26.2-15.9c0.9-0.5,2-0.4,2.8,0.3
l3.3,3.3c0.7,0.7,0.9,1.9,0.3,2.8L249.9,187c-0.2,0.3-0.4,0.4-0.7,0.5C248.9,187.5,248.6,187.4,248.4,187.2z M235.4,171.4
l13.5,13.5l15.3-25.2c0.1-0.1,0.1-0.3,0-0.3l-3.3-3.3c-0.1-0.1-0.2-0.1-0.3,0L235.4,171.4z" />
<path class="st2" d="M230.5,169.3c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l20.4,20.4l0,0c0.4,0.4,0.4,1,0,1.4c-0.4,0.4-1,0.4-1.4,0
L230.5,169.3L230.5,169.3z" />
</g>
</g>
<g>
<path class="st17" d="M184,108.7L196.6,96c3.9-3.9,10.3-3.9,14.1,0l22.6,22.6c2.7,2.7,3.6,6.7,2.5,10.3l-0.4,1.2
c-2.2,6.4-6.2,12.1-11.5,16.4l-2,1.7c-1.1,0.9-2.5-0.7-1.5-1.7l0,0" />
<path class="st1" d="M226,144.4l0.1,0.1l0,0c1.2-1.2,2.4-2.6,3.5-3.9c0,0,0.1-0.1,0.1-0.1c0.2-0.2,0.4-0.5,0.5-0.7
c0.1-0.1,0.1-0.1,0.1-0.2c0.2-0.2,0.3-0.5,0.5-0.7c0.1-0.1,0.1-0.2,0.2-0.2c0.1-0.2,0.3-0.5,0.5-0.7c0.1-0.1,0.1-0.1,0.1-0.2
c0.3-0.5,0.6-1,0.9-1.4c0,0,0.1-0.1,0.1-0.2c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.2,0.2-0.3,0.3-0.5c0.1-0.2,0.2-0.3,0.2-0.5
c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.1,0.2-0.3,0.2-0.5c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.1,0.1-0.3,0.2-0.5c0.1-0.2,0.2-0.5,0.3-0.7
c0-0.1,0.1-0.2,0.1-0.4c0.1-0.4,0.2-0.7,0.4-1.1l0.4-1.2c1.2-3.6,0.2-7.6-2.5-10.3l-22.5-22.5c-3.9-3.9-10.3-3.9-14.1,0l-2.8,2.8
l30.8,30.8C228.5,133.6,229.1,139.7,226,144.4z" />
<path class="st18" d="M209.2,110.1l-3.5-3.5c-1-1-1-2.5,0-3.5l0.7-0.7c1-1,2.5-1,3.5,0l3.5,3.5c1,1,1,2.5,0,3.5l-0.7,0.7
C211.7,111.1,210.2,111,209.2,110.1z M218,118.9l-3.5-3.5c-1-1-1-2.5,0-3.5l0.7-0.7c1-1,2.5-1,3.5,0l3.5,3.5c1,1,1,2.5,0,3.5
l-0.7,0.7C220.6,119.9,219,119.9,218,118.9z M233.5,134.4c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.1,0.2-0.3,0.2-0.5
c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.1,0.1-0.3,0.2-0.5c0.1-0.2,0.2-0.5,0.3-0.7c0-0.1,0.1-0.2,0.1-0.4c0.1-0.4,0.2-0.7,0.4-1.1l0.4-1.2
c0.1-0.2,0.1-0.4,0.2-0.7l-8.2-8.2c-1-1-2.5-1-3.5,0l-0.8,0.8c-1,1-1,2.5,0,3.5L233.5,134.4L233.5,134.4z" />
<path class="st2" d="M219.3,149.2c0,0-0.1-0.1-0.1-0.1c-1-1-0.9-2.6,0.1-3.6c0.6-0.6,1.5-0.6,2.1,0c0.2,0.2,0.4,0.5,0.4,0.8
l1.1-0.9c5.1-4.1,8.8-9.6,11-15.8l0.4-1.2c1-3.1,0.2-6.4-2.1-8.7l-22.6-22.6c-3.3-3.3-8.7-3.3-12,0L185,109.7
c-0.6,0.6-1.5,0.6-2.1,0c-0.6-0.6-0.6-1.5,0-2.1L195.5,95c4.5-4.5,11.8-4.5,16.3,0l22.6,22.6c3.1,3.1,4.2,7.6,2.8,11.8l-0.4,1.2
c-2.3,6.7-6.4,12.6-11.9,17.1l-2,1.7C221.7,150.2,220.3,150.1,219.3,149.2z M226.7,148.6c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4
c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1l-6.4,6.4C228.3,149.2,227.3,149.2,226.7,148.6z" />
<path class="st2" d="M210.8,139.1l-16.5-16.5c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0L213,137c0.6,0.6,0.6,1.5,0,2.1
C212.4,139.7,211.4,139.7,210.8,139.1z M186.8,115l-5.4-5.4c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l5.4,5.4
c0.6,0.6,0.6,1.5,0,2.1C188.3,115.6,187.4,115.6,186.8,115z M236.2,117.3l-23.4-23.4c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0
l23.4,23.4c0.6,0.6,0.6,1.5,0,2.1C237.7,117.9,236.8,117.9,236.2,117.3z M225.1,145.6c-0.6-0.6-0.6-1.4-0.1-2.1
c2.7-3.1,1.9-9.3-1.6-12.8l-30.8-30.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l30.8,30.8c4.6,4.6,5.4,12.6,1.7,16.9
C226.7,146.1,225.8,146.2,225.1,145.6C225.1,145.6,225.1,145.6,225.1,145.6z M198.7,98.9l-2.2-2.2c-0.6-0.6-0.6-1.5,0-2.1
s1.5-0.6,2.1,0l2.2,2.2c0.6,0.6,0.6,1.5,0,2.1C200.3,99.5,199.3,99.5,198.7,98.9z M203,97.5l-2.2-2.2c-0.6-0.6-0.6-1.5,0-2.1
c0.6-0.6,1.5-0.6,2.1,0l2.2,2.2c0.6,0.6,0.6,1.5,0,2.1C204.5,98.1,203.6,98.1,203,97.5z" />
<path class="st1" d="M219.2,138.5c-1.9-1.9-4.9-1.9-6.8,0c-1.9,1.9-1.9,4.9,0,6.8c0,0,0,0,0,0c1.9,1.9,4.9,1.9,6.8,0
C221,143.4,221,140.4,219.2,138.5C219.2,138.5,219.2,138.5,219.2,138.5z" />
<path class="st2" d="M211.3,146.4c-2.5-2.5-2.4-6.5,0-8.9c2.4-2.4,6.5-2.4,8.9,0c2.4,2.4,2.5,6.5,0,8.9
C217.8,148.9,213.8,148.8,211.3,146.4z M218.1,139.6c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0
C219.4,143,219.4,140.9,218.1,139.6z" />
<path class="st1" d="M188.2,114.3c-1.9,1.9-1.9,4.9,0,6.8c1.9,1.9,4.9,1.9,6.8,0c0.8-0.8,1.3-1.8,1.4-2.9l-5.3-5.3
C190,113.1,189,113.5,188.2,114.3z" />
<path class="st2" d="M187.2,122.2c-2.5-2.5-2.4-6.5,0-8.9c1-1,2.3-1.6,3.8-1.8c0.5,0,0.9,0.1,1.2,0.4l5.3,5.3
c0.3,0.3,0.5,0.8,0.4,1.2c-0.1,1.4-0.8,2.8-1.8,3.8C193.6,124.7,189.6,124.6,187.2,122.2z M190.6,114.6c-0.5,0.1-0.9,0.4-1.3,0.8
c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0c0.4-0.4,0.7-0.8,0.8-1.3L190.6,114.6z" />
<g>
<path class="st2" d="M217.7,98.1c-0.2-0.2-0.4-0.6-0.4-1l-0.2-5.2c0-0.8,0.6-1.5,1.4-1.6c0.8,0,1.5,0.6,1.6,1.4l0.2,5.2
c0,0.8-0.6,1.5-1.4,1.6C218.4,98.5,218,98.3,217.7,98.1z" />
</g>
<g>
<path class="st2" d="M219.3,147.6l-0.9-0.9c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l0.9,0.9c0.6,0.6,0.6,1.5,0,2.1
C220.8,148.2,219.9,148.2,219.3,147.6z" />
</g>
</g>
<g>
<path class="st1" d="M194.9,72.9l-1.1,1.1l-35.4-35.4l25.8-25.8c1.9-1.9,5.1-1.9,7.1,0l24.7,24.7l0,0
C214.9,47.8,202.2,65.6,194.9,72.9z" />
<path class="st5" d="M181,34.4l16.9-4.1c0.6-0.1,0.9,0.6,0.4,0.9l-10,6.6l3.4,3.4c0.3,0.3,0.1,0.8-0.3,0.8L176,44.4
c-0.5,0.1-0.8-0.6-0.4-0.9l8.3-5.1l-3.3-3.3C180.5,34.9,180.6,34.5,181,34.4z" />
<path class="st8" d="M209.4,52.7l-5.3-5.3c-2.5-2.5-2.5-6.6,0-9.2l2.8-2.8c0.6-0.6,1.5-0.6,2.1,0s0.6,1.5,0,2.1l-2.8,2.8
c-1.4,1.4-1.4,3.6,0,4.9l5.3,5.3c0.6,0.6,0.6,1.5,0,2.1C210.9,53.3,210,53.3,209.4,52.7z" />
<path class="st8" d="M192.8,75l-35.4-35.4c-0.6-0.6-0.6-1.5,0-2.1l25.8-25.8c2.5-2.5,6.6-2.5,9.2,0l24.7,24.7c0,0,0,0,0,0l2.8,2.8
c0.6,0.6,0.6,1.5,0,2.1c-0.6,0.6-1.5,0.6-2.1,0l-0.8-0.8c-2.9,11-14.6,26.8-21.1,33.3l-1.1,1.1C194.3,75.6,193.3,75.6,192.8,75z
M160.6,38.6l33.2,33.2c7.6-7.6,19.2-24.3,20.7-33.8l-24.2-24.2c-1.4-1.4-3.6-1.4-4.9,0C185.3,13.9,160.6,38.6,160.6,38.6z" />
<path class="st8" d="M192.8,75L156,38.2c-0.6-0.6-0.6-1.5,0-2.1s1.5-0.6,2.1,0l36.8,36.8c0.6,0.6,0.6,1.5,0,2.1
C194.3,75.6,193.3,75.6,192.8,75z" />
<path class="st1" d="M166,42.2c-1.9-1.9-4.9-1.9-6.8,0c-1.9,1.9-1.9,4.9,0,6.8c0,0,0,0,0,0c1.9,1.9,4.9,1.9,6.8,0
C167.9,47.2,167.9,44.1,166,42.2C166,42.2,166,42.2,166,42.2z M188.6,64.9c-1.9-1.9-4.9-1.9-6.8,0c-1.9,1.9-1.9,4.9,0,6.8
c0,0,0,0,0,0c1.9,1.9,4.9,1.9,6.8,0C190.5,69.8,190.5,66.7,188.6,64.9C188.6,64.9,188.6,64.9,188.6,64.9z" />
<path class="st8" d="M158.2,50.1c-2.5-2.5-2.4-6.5,0-8.9s6.5-2.4,8.9,0c2.4,2.4,2.4,6.5,0,8.9C164.6,52.5,160.6,52.6,158.2,50.1z
M164.9,43.3c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0C166.2,46.7,166.3,44.6,164.9,43.3z" />
<g>
<path class="st8" d="M180.8,72.7c-2.5-2.5-2.4-6.5,0-8.9c2.4-2.4,6.5-2.4,8.9,0c2.5,2.5,2.4,6.5,0,8.9
C187.3,75.2,183.3,75.2,180.8,72.7z M187.6,65.9c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7s3.4,1.3,4.7,0
C188.8,69.3,188.9,67.2,187.6,65.9z" />
</g>
<g>
<path class="st8" d="M180.8,72.7L151.1,43c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l29.7,29.7c0.6,0.6,0.6,1.5,0,2.1
C182.3,73.3,181.4,73.3,180.8,72.7z" />
</g>
<g>
<path class="st8" d="M146.1,38.1c-0.1-0.1-0.1-0.1-0.2-0.2c0-0.1-0.1-0.2-0.1-0.3c0-0.1-0.1-0.2-0.1-0.3c0-0.1,0-0.2,0-0.3
c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0.1-0.3c0.1,0,0.1-0.1,0.2-0.2c0.1-0.1,0.1-0.2,0.2-0.2s0.1-0.1,0.2-0.2c0.1-0.1,0.2-0.1,0.3-0.1
s0.2,0,0.3-0.1s0.2,0,0.3,0c0.1,0,0.2,0,0.3,0c0.1,0,0.2,0,0.3,0.1s0.2,0.1,0.3,0.1c0.2,0.1,0.3,0.2,0.4,0.4
c0.1,0.1,0.1,0.2,0.1,0.3c0,0.1,0.1,0.2,0.1,0.3c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2-0.1,0.3c0,0.1-0.1,0.2-0.1,0.3
c-0.1,0.1-0.1,0.2-0.2,0.2s-0.1,0.1-0.2,0.2c-0.1,0.1-0.2,0.1-0.3,0.1s-0.2,0.1-0.3,0.1c-0.1,0-0.2,0-0.3,0c-0.1,0-0.2,0-0.3,0
c-0.1,0-0.2,0-0.3-0.1c-0.1,0-0.2-0.1-0.3-0.1C146.3,38.3,146.2,38.1,146.1,38.1z" />
</g>
</g>
<path class="st2"
d="M30.2,31.2l3.1,3.1l0,0c0,0,0,0,0,0c-2,1.9-3.9,3.4-6.2,1.8L26.8,36l-0.6-0.6" />
<path class="st2" d="M32.6,35L29.5,32c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l3.1,3.1c0.4,0.4,0.4,1,0,1.4S33,35.4,32.6,35z M37.7,23.8
l3.1,3.1l0,0c0,0,0,0,0,0c-2,1.9-3.9,3.4-6.2,1.8l-0.3-0.2l-0.6-0.6" />
<path class="st2" d="M40,27.6L37,24.5c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l3.1,3.1c0.4,0.4,0.4,1,0,1.4S40.5,28,40,27.6z M20.8,21.8
l-3.1-3.1l0,0c0,0,0,0,0,0c-1.9,2-3.4,3.9-1.8,6.2l0.2,0.3l0.6,0.6" />
<path class="st2" d="M20.1,22.5L17,19.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l3.1,3.1c0.4,0.4,0.4,1,0,1.4S20.4,22.9,20.1,22.5z
M28.2,14.3l-3.1-3.1l0,0c0,0,0,0,0,0c-1.9,2-3.4,3.9-1.8,6.2l0.2,0.3l0.6,0.6" />
<path class="st2"
d="M27.5,15L24.4,12c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l3.1,3.1c0.4,0.4,0.4,1,0,1.4C28.6,15.5,27.9,15.4,27.5,15z" />
<g>
<path class="st1" d="M20.3,36.7L15.7,32c-1.3-1.3-1.3-3.5,0-4.8l13.2-13.2c1.3-1.3,3.5-1.3,4.8,0l4.7,4.7c1.3,1.3,1.3,3.5,0,4.8
L25.1,36.7C23.8,38,21.7,38,20.3,36.7z" />
</g>
<g>
<path class="st15" d="M33.6,18.5c-1.5-1.5-3.9-1.5-5.3,0c-1.5,1.5-1.5,3.9,0,5.3c0,0,0,0,0,0c1.5,1.5,3.9,1.5,5.3,0
S35,19.9,33.6,18.5C33.6,18.5,33.6,18.5,33.6,18.5z" />
</g>
<g>
<path class="st19" d="M26,26c-1.5-1.5-3.9-1.5-5.3,0s-1.5,3.9,0,5.3c0,0,0,0,0,0c1.5,1.5,3.9,1.5,5.3,0C27.5,29.9,27.5,27.5,26,26
C26,26,26,26,26,26z" />
</g>
<g>
<path class="st2" d="M19.6,37.4l-4.7-4.7c-1.7-1.7-1.7-4.5,0-6.2l13.2-13.2c1.7-1.7,4.5-1.7,6.2,0l4.7,4.7c1.7,1.7,1.7,4.5,0,6.2
L25.9,37.4C24.1,39.1,21.4,39.1,19.6,37.4z M33,14.7c-0.9-0.9-2.4-0.9-3.4,0L16.4,27.9c-0.9,0.9-0.9,2.4,0,3.4l4.7,4.7
c0.9,0.9,2.4,0.9,3.4,0l13.2-13.2c0.9-0.9,0.9-2.4,0-3.4L33,14.7z" />
</g>
<g>
<path class="st2" d="M10.3,41.7c-0.4-0.4-0.4-1,0-1.4l6.7-6.7c0.4-0.4,1-0.4,1.4,0c0.4,0.4,0.4,1,0,1.4l-6.7,6.7
C11.4,42.1,10.7,42.1,10.3,41.7z" />
</g>
<g>
<path class="st20"
d="M282.1,91.1l-24.8-24.8l-16.5,16.5c-1.5,1.5-1.5,4,0,5.5l19.3,19.3c1.5,1.5,4,1.5,5.5,0L282.1,91.1z" />
<path class="st1" d="M256.4,84.9l13.2-3.2c0.4-0.1,0.7,0.5,0.3,0.7l-7.8,5.2l2.7,2.7c0.2,0.2,0.1,0.6-0.2,0.7l-12,1.9
c-0.4,0.1-0.6-0.5-0.3-0.7l6.5-3.9l-2.6-2.6C256,85.4,256.1,85,256.4,84.9z" />
<path class="st21" d="M279.4,88.4l-16,16c-0.3,0.3-0.8,0.3-1.1,0l-11.8-11.8c-0.8-0.8-2-0.8-2.8,0l0,0c-0.8,0.8-0.8,2,0,2.8
l12.4,12.4c1.5,1.5,4,1.5,5.5,0l16.5-16.5L279.4,88.4z" />
<path class="st1" d="M259.3,61.7l27.5,27.5l-3.3,3.3L256,65L259.3,61.7z M267.5,69.9l3.3-3.3l-4.1-4.1l-3.3,3.3 M282.7,85.1
l3.3-3.3l-4.1-4.1l-3.3,3.3" />
<path class="st7" d="M259.3,108.5L240,89.2c-2-2-2-5.2,0-7.2l13.8-13.8c0.5-0.5,1.2-0.5,1.7,0c0.5,0.5,0.5,1.2,0,1.7l-13.8,13.8
c-1.1,1.1-1.1,2.8,0,3.9l19.3,19.3c1.1,1.1,2.8,1.1,3.9,0l16.5-16.5c0.5-0.5,1.2-0.5,1.7,0c0.5,0.5,0.5,1.2,0,1.7l-16.5,16.5
C264.4,110.4,261.3,110.4,259.3,108.5z" />
<path class="st1" d="M251,78.2c-0.5-0.5-0.5-1.2,0-1.7l8.3-8.3c0.5-0.5,1.2-0.5,1.7,0c0.5,0.5,0.5,1.2,0,1.7l-8.3,8.3
C252.2,78.6,251.5,78.6,251,78.2z M246.9,82.3c-0.1-0.1-0.1-0.1-0.1-0.2c-0.1-0.1-0.1-0.1-0.1-0.2c0-0.1,0-0.1-0.1-0.2
c0-0.1,0-0.1,0-0.2c0-0.1,0-0.2,0-0.2c0-0.1,0-0.1,0.1-0.2c0-0.1,0.1-0.1,0.1-0.2c0-0.1,0.1,0,0.1-0.1c0.1-0.1,0.1-0.1,0.2-0.1
c0.1,0,0.1-0.1,0.2-0.1s0.2-0.1,0.2-0.1c0.1,0,0.1,0,0.2,0c0.1,0,0.2,0,0.2,0s0.1,0,0.2,0.1c0.1,0,0.1,0.1,0.2,0.1
c0.3,0.2,0.5,0.6,0.5,1c0,0.1,0,0.2,0,0.2c0,0.1,0,0.1-0.1,0.2c0,0.1-0.1,0.1-0.1,0.2s-0.2,0.1-0.2,0.1c-0.2,0.2-0.5,0.4-0.8,0.3
C247.4,82.7,247.1,82.5,246.9,82.3z" />
<path class="st7" d="M282.7,93.3l-27.5-27.5c-0.5-0.5-0.5-1.2,0-1.7l3.3-3.3c0.5-0.5,1.2-0.5,1.7,0l27.5,27.5
c0.5,0.5,0.5,1.2,0,1.7l-3.3,3.3C283.9,93.8,283.1,93.8,282.7,93.3z M257.6,65l25.9,25.9l1.7-1.7l-25.9-25.9L257.6,65z" />
<path class="st7" d="M266.7,70.7c-0.5-0.5-0.5-1.2,0-1.7l2.5-2.5l-2.5-2.5l-0.3,0.3c-0.5,0.5-1.2,0.5-1.7,0c-0.5-0.5-0.5-1.2,0-1.7
l1.1-1.1c0.5-0.5,1.2-0.5,1.7,0l4.1,4.1c0.5,0.5,0.5,1.2,0,1.7l-3.3,3.3C267.9,71.2,267.2,71.2,266.7,70.7z M281.8,85.9
c-0.5-0.5-0.5-1.2,0-1.7l2.5-2.5l-2.5-2.5l-0.3,0.3c-0.5,0.5-1.2,0.5-1.7,0c-0.5-0.5-0.5-1.2,0-1.7l1.1-1.1c0.5-0.5,1.2-0.5,1.7,0
l4.1,4.1c0.5,0.5,0.5,1.2,0,1.7l-3.3,3.3C283,86.3,282.3,86.3,281.8,85.9z" />
</g>
<g>
<path class="st11" d="M308.8,72.2l34.3,34.3l3.8-3.8c2.1-2.1,2.1-5.5,0-7.6l-13.6-13.6l-0.6-0.2c-2.3-0.8-3-3.7-1.3-5.5l-1.2-1.2
C324,68.5,316.8,68.8,308.8,72.2" />
<path class="st12" d="M319.7,69.9C319.7,69.8,319.7,69.8,319.7,69.9c-0.4-0.1-0.7-0.1-1-0.1c-3.1,0.1-6.4,1-9.9,2.4l0,0l10.9,10.9
C323.3,79.5,323.3,73.5,319.7,69.9 M348.4,98.8c-3.6-2.7-8.8-2.4-12.2,0.9l6.8,6.8l3.8-3.8C347.9,101.6,348.4,100.2,348.4,98.8" />
<path class="st2" d="M342,107.6l-34.3-34.3c-0.4-0.4-0.5-0.8-0.4-1.3c0.1-0.5,0.4-0.9,0.9-1.1c9.8-4.2,17.1-3.3,23.1,2.8l0.1,0.1
c0.6,0.6,0.6,1.5,0,2.1c-0.6,0.6-1.5,0.6-2.1,0l-0.1-0.1c-4.7-4.7-10.3-5.7-17.8-2.9l31.6,31.6l2.8-2.8c1.5-1.5,1.5-4,0-5.5
L333.7,84c-0.4-0.4-0.5-0.8-0.4-1.3l0.6-3.8c0.1-0.8,0.9-1.4,1.7-1.2c0.8,0.1,1.4,0.9,1.2,1.7l-0.5,3L348,94c2.7,2.7,2.7,7.1,0,9.8
l-3.8,3.8C343.6,108.2,342.6,108.2,342,107.6z" />
<path class="st1" d="M316.9,72.7c-2.1-2.1-5.5-2.1-7.6,0c-2.1,2.1-2.1,5.5,0,7.6c0,0,0,0,0,0c2.1,2.1,5.5,2.1,7.6,0
C319,78.2,319,74.8,316.9,72.7C316.9,72.7,316.9,72.7,316.9,72.7z M336.7,88.2c-1.2-1.2-3.1-1.2-4.2,0c-1.2,1.2-1.2,3.1,0,4.2
s3.1,1.2,4.2,0C337.8,91.2,337.8,89.3,336.7,88.2z" />
<path class="st2" d="M308.2,81.4c-2.7-2.7-2.7-7.1,0-9.8s7.1-2.7,9.8,0s2.7,7.1,0,9.8C315.2,84.1,310.9,84.1,308.2,81.4z
M315.8,73.7c-1.5-1.5-4-1.5-5.5,0c-1.5,1.5-1.5,4,0,5.5c1.5,1.5,4,1.5,5.5,0C317.3,77.7,317.3,75.3,315.8,73.7z" />
<path class="st1" d="M346.7,102.5c-2.1-2.1-5.5-2.1-7.6,0c-2.1,2.1-2.1,5.5,0,7.6c0,0,0,0,0,0c2.1,2.1,5.5,2.1,7.6,0
S348.8,104.6,346.7,102.5C346.7,102.5,346.7,102.5,346.7,102.5z" />
<path class="st2" d="M338,111.2c-2.7-2.7-2.7-7.1,0-9.8s7.1-2.7,9.8,0c2.7,2.7,2.7,7.1,0,9.8C345.1,113.9,340.7,113.9,338,111.2z
M345.7,103.6c-1.5-1.5-4-1.5-5.5,0c-1.5,1.5-1.5,4,0,5.5c1.5,1.5,4,1.5,5.5,0C347.2,107.6,347.2,105.1,345.7,103.6z" />
<path class="st2" d="M338,111.2l-34.5-34.5c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l34.5,34.5c0.6,0.6,0.6,1.5,0,2.1
C339.5,111.8,338.6,111.8,338,111.2z M299.3,72.5c-0.3-0.3-0.5-0.7-0.4-1.1c0-0.4,0.1-0.8,0.4-1.1s0.7-0.5,1.1-0.4
c0.8,0,1.5,0.7,1.5,1.5c0,0.4-0.1,0.8-0.4,1.1c-0.3,0.3-0.7,0.5-1.1,0.4C300,73,299.6,72.8,299.3,72.5z" />
</g>
<g>
<path class="st22" d="M277.7,28.4c0-3,2.4-5.4,5.4-5.4s5.4,2.4,5.4,5.4s-2.4,5.4-5.4,5.4 M264.6,15.2c0-3,2.4-5.4,5.4-5.4
c3,0,5.4,2.4,5.4,5.4s-2.4,5.4-5.4,5.4" />
<path class="st15" d="M275,44.3l-21-21c-0.2-0.2-0.2-0.6,0-0.9l6.8-6.8c0.2-0.2,0.6-0.2,0.9,0l21,21c0.2,0.2,0.2,0.6,0,0.9
l-6.8,6.8C275.6,44.6,275.2,44.6,275,44.3z" />
<path class="st1" d="M255.1,21.4l-1.1,1.1c-0.2,0.2-0.2,0.6,0,0.9l1.8,1.8L270,24l0,0l-3.8-3.8L255.1,21.4z M259.2,28.6l-0.2-0.2
l4,4l14.2-1.1l0,0l-3.8-3.8L259.2,28.6z M281.6,38.7l1.1-1.1c0.2-0.2,0.2-0.6,0-0.9l-1.8-1.8L266.7,36l0,0l3.7,3.7L281.6,38.7z
M271.6,13.6c-0.9-0.9-2.4-0.9-3.3,0s-0.9,2.4,0,3.3c0.9,0.9,2.4,0.9,3.3,0C272.5,16,272.5,14.5,271.6,13.6z M284.8,26.7
c-0.9-0.9-2.4-0.9-3.3,0s-0.9,2.4,0,3.3s2.4,0.9,3.3,0S285.7,27.6,284.8,26.7z" />
<path class="st2" d="M274.3,45l-21-21c-0.6-0.6-0.6-1.6,0-2.2l6.8-6.8c0.6-0.6,1.6-0.6,2.2,0l21,21c0.6,0.6,0.6,1.6,0,2.2l-6.8,6.8
C275.9,45.6,275,45.6,274.3,45z M254.9,22.9l20.6,20.6l6.3-6.3l-20.6-20.6L254.9,22.9z M250.3,34.9c-0.4-0.4-0.4-0.9,0-1.3l5-5
c0.4-0.4,0.9-0.4,1.3,0c0.4,0.4,0.4,0.9,0,1.3l-5,5C251.2,35.3,250.7,35.3,250.3,34.9z M263.4,48.1c-0.4-0.4-0.4-0.9,0-1.3l5-5
c0.4-0.4,0.9-0.4,1.3,0c0.4,0.4,0.4,0.9,0,1.3l-5,5C264.4,48.4,263.8,48.4,263.4,48.1z" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 34 KiB

-420
View File
@@ -1,420 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
viewBox="0 0 360 200" style="enable-background:new 0 0 360 200;" xml:space="preserve">
<style type="text/css">
.st0{fill:#E6E6E6;}
.st1{fill:#DCDCDC;}
.st2{fill:#FFFFFF;}
.st3{fill:#808080;}
.st4{fill:#E8E8E8;}
.st5{fill:#99F5C8;}
.st6{opacity:0.26;fill:#1D1D1B;}
.st7{fill:#C8C8C8;}
.st8{fill:#B5B5B5;}
.st9{fill:#878787;}
.st10{fill:#666666;}
.st11{fill:#D1D1D1;}
.st12{opacity:0.2;fill:#808080;enable-background:new ;}
</style>
<rect class="st0" width="360" height="200" />
<g>
<path class="st1" d="M47.7,59.7l3,3l-1.9,1.9l7.6,7.6l3.7-3.7l-7.6-7.6l-1.8,1.8" />
<path class="st2"
d="M51.1,130.2L13.4,92.5c-2.1-2.1-2.1-5.4,0-7.5L32,66.3c2-2,5.4-2,7.5,0L77.2,104" />
<path class="st2" d="M46.3,85.9L32.9,99.3c-1,1-2.7,1-3.7,0l-8.7-8.7c-1-1-1-2.7,0-3.7l13.4-13.4c1-1,2.7-1,3.7,0l8.7,8.7
C47.4,83.2,47.3,84.9,46.3,85.9z" />
<path class="st3" d="M78.5,105.3c-0.7,0.7-1.9,0.7-2.6,0L38.2,67.7c-1.3-1.3-3.6-1.4-4.9,0L14.7,86.3c-1.3,1.3-1.3,3.5,0,4.9
l37.7,37.7c0.7,0.7,0.7,1.9,0,2.6c-0.7,0.7-1.9,0.7-2.6,0L12.1,93.8c-2.8-2.8-2.8-7.3,0-10.1L30.7,65c2.8-2.8,7.3-2.8,10.1,0
l37.7,37.7C79.2,103.5,79.2,104.6,78.5,105.3z" />
<path class="st4" d="M46.3,85.9L32.9,99.3c-1,1-2.7,1-3.7,0l-8.7-8.7c-1-1-1-2.7,0-3.7l13.4-13.4c1-1,2.7-1,3.7,0l8.7,8.7
C47.4,83.2,47.3,84.9,46.3,85.9z" />
<path class="st3" d="M47.6,87.2l-13.4,13.4c-1.7,1.7-4.6,1.7-6.3,0l-8.7-8.7c-1.7-1.7-1.7-4.6,0-6.3l13.4-13.4
c1.7-1.7,4.6-1.7,6.3,0l8.7,8.7C49.4,82.6,49.4,85.4,47.6,87.2z M21.8,88.2c-0.3,0.3-0.3,0.8,0,1.1l8.7,8.7c0.3,0.3,0.8,0.3,1.1,0
L45,84.6c0.3-0.3,0.3-0.8,0-1.1l-8.7-8.7c-0.3-0.3-0.8-0.3-1.1,0L21.8,88.2z M82.2,101.6l-33.5,33.5c-0.7,0.7-1.9,0.7-2.6,0
c-0.7-0.7-0.7-1.9,0-2.6L79.6,99c0.7-0.7,1.9-0.7,2.6,0S82.9,100.9,82.2,101.6z M80.3,95.9c-1.7,1.7-4.6,1.7-6.3,0L58.9,80.9
c-0.3-0.3-0.8-0.3-1.1,0l-1.9,1.9c-0.7,0.7-1.9,0.7-2.6,0c-0.7-0.7-0.7-1.9,0-2.6l1.9-1.9c1.7-1.7,4.6-1.7,6.3,0l15.1,15.1
c0.3,0.3,0.8,0.3,1.1,0c0.3-0.3,0.3-0.8,0-1.1L57,71.5c-0.7-0.7-0.7-1.9,0-2.6s1.9-0.7,2.6,0l20.7,20.7C82,91.3,82,94.2,80.3,95.9z
" />
<path class="st3" d="M61.4,69.7l-3.7,3.7c-0.7,0.7-1.9,0.7-2.6,0l-7.6-7.6c-0.7-0.7-0.7-1.9,0-2.6l0.6-0.6L46.4,61
c-0.7-0.7-0.7-1.9,0-2.6s1.9-0.7,2.6,0l1.7,1.7l0.5-0.5c0.7-0.7,1.9-0.7,2.6,0l7.5,7.5C62,67.8,62.1,69,61.4,69.7z M56.4,69.5
l1.1-1.1l-4.9-4.9l-1.1,1.1L56.4,69.5z" />
<g>
<path class="st5" d="M57.6,114.1c-0.2,0.2-0.5,0.4-0.9,0.5c-1,0.2-2-0.4-2.2-1.3l-1.3-5.7l-1.3,1.9c-0.4,0.6-1.1,0.9-1.8,0.7
c-0.7-0.2-1.3-0.7-1.4-1.4l-2.3-9.5c-0.2-1,0.4-2,1.3-2.2c1-0.2,2,0.4,2.2,1.3l1.3,5.7l1.3-1.9c0.4-0.6,1.1-0.9,1.8-0.7
c0.7,0.2,1.3,0.7,1.4,1.4l2.2,9.5C58.2,113.1,58,113.7,57.6,114.1z" />
</g>
</g>
<g>
<path class="st2" d="M29.9,146.1l24,24c1,1,1,2.5,0,3.5l-14.1,14.1c-1,1-2.5,1-3.5,0l-24-24c-1-1-1-2.5,0-3.5l14.1-14.1
C27.4,145.1,28.9,145.1,29.9,146.1z M11.5,152.4l7.1-7.1l4.2,4.2l-7.1,7.1L11.5,152.4z" />
<path class="st5" d="M37,153.1l17,17c1,1,1,2.5,0,3.5l-14.1,14.1c-1,1-2.5,1-3.5,0l-17-17c-1-1-1-2.5,0-3.5l14.1-14.1
C34.4,152.1,36,152.1,37,153.1z" />
<path class="st6" d="M50.4,170.1L50.4,170.1c1-1,1-2.5,0-3.5L37,153.1c-1-1-2.5-1-3.5,0l-1.8,1.8l15.2,15.2
C47.9,171.1,49.4,171.1,50.4,170.1z" />
<path class="st3" d="M55,174.7l-14.1,14.1c-1.6,1.6-4.1,1.6-5.7,0l-24-24c-1.6-1.6-1.6-4.1,0-5.7L25.3,145c1.6-1.6,4.1-1.6,5.7,0
l24,24C56.6,170.6,56.6,173.1,55,174.7z M13.3,161.3c-0.4,0.4-0.4,1,0,1.4l24,24c0.4,0.4,1,0.4,1.4,0l14.1-14.1
c0.4-0.4,0.4-1,0-1.4l-24-24c-0.4-0.4-1-0.4-1.4,0L13.3,161.3z" />
<path class="st2" d="M39.1,183.5c-0.6,0.6-1.5,0.6-2.1,0l-8.8-8.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l8.8,8.8
C39.7,182,39.7,182.9,39.1,183.5z M25,169.4c-0.3,0.3-0.7,0.5-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4c-0.3-0.3-0.5-0.7-0.4-1.1
c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0.1-0.3s0.1-0.2,0.1-0.3c0.3-0.4,0.7-0.7,1.2-0.7c0.4,0,0.8,0.1,1.1,0.4c0.1,0.1,0.1,0.1,0.2,0.2
c0.1,0.1,0.1,0.2,0.1,0.3c0,0.1,0.1,0.2,0.1,0.3c0,0.1,0,0.2,0,0.3C25.4,168.7,25.2,169.1,25,169.4z" />
<path class="st3" d="M23.9,150.6c-0.6,0.6-1.5,0.6-2.1,0l-3.2-3.2l-4.9,4.9l0.4,0.4c0.6,0.6,0.6,1.5,0,2.1s-1.5,0.6-2.1,0l-1.4-1.4
c-0.6-0.6-0.6-1.5,0-2.1l7.1-7.1c0.6-0.6,1.5-0.6,2.1,0l4.2,4.2C24.5,149.1,24.5,150,23.9,150.6z" />
</g>
<g>
<path class="st2" d="M117.2,186.9l-1.1,1.1l-35.4-35.4l25.8-25.8c1.9-1.9,5.1-1.9,7.1,0l24.7,24.7l0,0
C137.2,161.8,124.5,179.6,117.2,186.9z" />
<path class="st5" d="M103.2,148.4l16.9-4.1c0.6-0.1,0.9,0.6,0.4,0.9l-10,6.6l3.4,3.4c0.3,0.3,0.1,0.8-0.3,0.8l-15.4,2.4
c-0.5,0.1-0.8-0.6-0.4-0.9l8.3-5.1l-3.3-3.3C102.8,148.9,102.9,148.5,103.2,148.4z" />
<path class="st3" d="M131.7,166.8l-5.3-5.3c-2.5-2.5-2.5-6.6,0-9.2l2.8-2.8c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1l-2.8,2.8
c-1.4,1.4-1.4,3.6,0,4.9l5.3,5.3c0.6,0.6,0.6,1.5,0,2.1S132.2,167.3,131.7,166.8z" />
<path class="st3" d="M115.1,189l-35.4-35.4c-0.6-0.6-0.6-1.5,0-2.1l25.8-25.8c2.5-2.5,6.6-2.5,9.2,0l24.7,24.7c0,0,0,0,0,0l2.8,2.8
c0.6,0.6,0.6,1.5,0,2.1s-1.5,0.6-2.1,0l-0.8-0.8c-2.9,11-14.6,26.8-21.1,33.3l-1.1,1.1C116.6,189.6,115.6,189.6,115.1,189z
M82.9,152.6l33.2,33.2c7.6-7.6,19.2-24.3,20.7-33.8l-24.2-24.2c-1.4-1.4-3.6-1.4-4.9,0C107.6,127.9,82.9,152.6,82.9,152.6z" />
<path class="st3" d="M115.1,189l-36.8-36.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l36.8,36.8c0.6,0.6,0.6,1.5,0,2.1
C116.6,189.6,115.6,189.6,115.1,189z" />
<path class="st2" d="M88.3,156.3c-1.9-1.9-4.9-1.9-6.8,0s-1.9,4.9,0,6.8c0,0,0,0,0,0c1.9,1.9,4.9,1.9,6.8,0S90.2,158.1,88.3,156.3
C88.3,156.3,88.3,156.3,88.3,156.3z M110.9,178.9c-1.9-1.9-4.9-1.9-6.8,0c-1.9,1.9-1.9,4.9,0,6.8c0,0,0,0,0,0
c1.9,1.9,4.9,1.9,6.8,0C112.8,183.8,112.8,180.8,110.9,178.9C110.9,178.9,110.9,178.9,110.9,178.9z" />
<path class="st3" d="M80.4,164.1c-2.5-2.5-2.4-6.5,0-8.9c2.4-2.4,6.5-2.4,8.9,0c2.4,2.4,2.4,6.5,0,8.9
C86.9,166.6,82.9,166.6,80.4,164.1z M87.2,157.3c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0
C88.5,160.7,88.5,158.6,87.2,157.3z" />
<g>
<path class="st3" d="M103.1,186.7c-2.5-2.5-2.4-6.5,0-8.9s6.5-2.4,8.9,0c2.5,2.5,2.4,6.5,0,8.9
C109.5,189.2,105.5,189.2,103.1,186.7z M109.9,180c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0
S111.2,181.3,109.9,180z" />
</g>
<g>
<path class="st3" d="M103.1,186.7L73.4,157c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l29.7,29.7c0.6,0.6,0.6,1.5,0,2.1
C104.6,187.3,103.7,187.3,103.1,186.7z" />
</g>
<g>
<path class="st3" d="M68.4,152.1c-0.1-0.1-0.1-0.1-0.2-0.2s-0.1-0.2-0.1-0.3s-0.1-0.2-0.1-0.3c0-0.1,0-0.2,0-0.3
c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0.1-0.3c0.1,0,0.1-0.1,0.2-0.2s0.1-0.2,0.2-0.2s0.1-0.1,0.2-0.2c0.1-0.1,0.2-0.1,0.3-0.1
s0.2,0,0.3-0.1s0.2,0,0.3,0c0.1,0,0.2,0,0.3,0c0.1,0,0.2,0,0.3,0.1c0.1,0,0.2,0.1,0.3,0.1c0.2,0.1,0.3,0.2,0.4,0.4
c0.1,0.1,0.1,0.2,0.1,0.3s0.1,0.2,0.1,0.3c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2-0.1,0.3s-0.1,0.2-0.1,0.3
c-0.1,0.1-0.1,0.2-0.2,0.2s-0.1,0.1-0.2,0.2c-0.1,0.1-0.2,0.1-0.3,0.1c-0.1,0-0.2,0.1-0.3,0.1c-0.1,0-0.2,0-0.3,0
c-0.1,0-0.2,0-0.3,0s-0.2,0-0.3-0.1c-0.1,0-0.2-0.1-0.3-0.1C68.6,152.3,68.5,152.2,68.4,152.1z" />
</g>
</g>
<g>
<path class="st2" d="M336.4,33.8l-13.6,13.6c-1.9,1.9-5.1,1.9-7.1,0l-13.8-13.8c-1.9-1.9-1.9-5.1,0-7.1L315.5,13
c1.9-1.9,5.1-1.9,7.1,0l13.8,13.8C338.3,28.7,338.3,31.9,336.4,33.8z" />
<path class="st3" d="M337.4,34.9l-13.6,13.6c-2.5,2.5-6.6,2.5-9.2,0l-13.8-13.8c-2.5-2.5-2.5-6.6,0-9.2l13.6-13.6
c2.5-2.5,6.6-2.5,9.2,0l13.8,13.8C340,28.2,340,32.3,337.4,34.9z M303,27.6c-1.4,1.4-1.4,3.6,0,4.9l13.8,13.8
c1.4,1.4,3.6,1.4,4.9,0l13.6-13.6c1.4-1.4,1.4-3.6,0-4.9L321.5,14c-1.4-1.4-3.6-1.4-4.9,0L303,27.6z" />
<path class="st2" d="M325.4,36.4L325.4,36.4c-3.4,3.4-9,3.4-12.4,0l-0.1-0.1c-3.4-3.4-3.4-9,0-12.4l0,0c3.4-3.4,9-3.4,12.4,0
l0.1,0.1C328.8,27.5,328.8,33,325.4,36.4z" />
<path class="st3" d="M326.5,37.5c-4,4-10.5,4-14.5,0l-0.1-0.1c-4-4-4-10.5,0-14.5s10.5-4,14.5,0l0.1,0.1
C330.5,27,330.5,33.5,326.5,37.5z M314,25c-2.8,2.8-2.8,7.4,0,10.3l0.1,0.1c2.8,2.8,7.4,2.8,10.3,0c2.8-2.8,2.8-7.4,0-10.3
l-0.1-0.1C321.4,22.2,316.8,22.2,314,25z" />
<path class="st3" d="M315.3,31.5c-0.7,0.7-0.7,1.8,0,2.5c0.7,0.7,1.8,0.7,2.5,0c0,0,0,0,0,0c0.7-0.7,0.7-1.8,0-2.5
C317.2,30.8,316,30.8,315.3,31.5C315.3,31.5,315.3,31.5,315.3,31.5z M320.4,26.4c-0.7,0.7-0.7,1.8,0,2.5s1.8,0.7,2.5,0c0,0,0,0,0,0
c0.7-0.7,0.7-1.8,0-2.5C322.3,25.7,321.1,25.7,320.4,26.4C320.4,26.4,320.4,26.4,320.4,26.4z" />
<path class="st2"
d="M341.7,43.3l3.8,3.8c2.6,2.6,2.6,6.8,0,9.4l0,0c-2.6,2.6-6.8,2.6-9.4,0l-3.8-3.8L341.7,43.3z" />
<path class="st3" d="M346.6,57.6c-3.2,3.2-8.3,3.2-11.5,0l-3.1-3.1c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l3.1,3.1
c2,2,5.3,2,7.3,0c2-2,2-5.3,0-7.3l-3.1-3.1c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l3.1,3.1
C349.8,49.3,349.8,54.4,346.6,57.6z" />
<path class="st3" d="M335.1,51.8c-0.6,0.6-1.5,0.6-2.1,0l-3.3-3.3c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l3.3,3.3
C335.7,50.3,335.7,51.2,335.1,51.8z M340.8,46.2c-0.6,0.6-1.5,0.6-2.1,0l-3.3-3.3c-0.6-0.6-0.6-1.5,0-2.1s1.5-0.6,2.1,0l3.3,3.3
C341.4,44.6,341.4,45.6,340.8,46.2z" />
<path class="st3" d="M344.1,43.1l-12,12c-0.6,0.6-1.5,0.6-2.1,0s-0.6-1.5,0-2.1l12-12c0.6-0.6,1.5-0.6,2.1,0S344.7,42.5,344.1,43.1
z M349.6,60.6c-0.6,0.6-1.5,0.6-2.1,0l-3-3c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l3,3C350.1,59,350.2,60,349.6,60.6z" />
<g>
<path class="st5" d="M339.8,50.8c-0.5,0.5-0.5,1.4,0,1.9c0.5,0.5,1.4,0.5,1.9,0c0,0,0,0,0,0c0.5-0.5,0.5-1.4,0-1.9
C341.2,50.3,340.3,50.3,339.8,50.8C339.8,50.8,339.8,50.8,339.8,50.8z" />
</g>
</g>
<g>
<path class="st7" d="M171,149l34.3,34.3l3.8-3.8c2.1-2.1,2.1-5.5,0-7.6l-13.6-13.6l-0.6-0.2c-2.3-0.8-3-3.7-1.3-5.5l-1.2-1.2
C186.3,145.2,179.1,145.5,171,149" />
<path class="st8" d="M182,146.6C181.9,146.6,181.9,146.6,182,146.6c-0.4-0.1-0.7-0.1-1-0.1c-3.1,0.1-6.4,1-9.9,2.4l0,0l10.9,10.9
C185.6,156.3,185.6,150.3,182,146.6 M210.7,175.6c-3.6-2.7-8.8-2.4-12.2,0.9l6.8,6.8l3.8-3.8C210.2,178.4,210.7,177,210.7,175.6" />
<path class="st3" d="M204.3,184.4L170,150c-0.4-0.4-0.5-0.8-0.4-1.3c0.1-0.5,0.4-0.9,0.9-1.1c9.8-4.2,17.1-3.3,23.1,2.8l0.1,0.1
c0.6,0.6,0.6,1.5,0,2.1c-0.6,0.6-1.5,0.6-2.1,0l-0.1-0.1c-4.7-4.7-10.3-5.7-17.8-2.9l31.6,31.6l2.8-2.8c1.5-1.5,1.5-4,0-5.5
l-12.1-12.1c-0.4-0.4-0.5-0.8-0.4-1.3l0.6-3.8c0.1-0.8,0.9-1.4,1.7-1.2c0.8,0.1,1.4,0.9,1.2,1.7l-0.5,3l11.6,11.6
c2.7,2.7,2.7,7.1,0,9.8l-3.8,3.8C205.8,185,204.9,184.9,204.3,184.4z" />
<path class="st2" d="M179.1,149.4c-2.1-2.1-5.5-2.1-7.6,0c-2.1,2.1-2.1,5.5,0,7.6c0,0,0,0,0,0c2.1,2.1,5.5,2.1,7.6,0
C181.3,155,181.3,151.6,179.1,149.4C179.1,149.4,179.1,149.4,179.1,149.4z M198.9,164.9c-1.2-1.2-3.1-1.2-4.2,0s-1.2,3.1,0,4.2
s3.1,1.2,4.2,0C200.1,168,200.1,166.1,198.9,164.9z" />
<path class="st3" d="M170.4,158.1c-2.7-2.7-2.7-7.1,0-9.8c2.7-2.7,7.1-2.7,9.8,0c2.7,2.7,2.7,7.1,0,9.8
C177.5,160.8,173.1,160.8,170.4,158.1z M178.1,150.5c-1.5-1.5-4-1.5-5.5,0s-1.5,4,0,5.5s4,1.5,5.5,0
C179.6,154.5,179.6,152,178.1,150.5z" />
<path class="st2" d="M209,179.3c-2.1-2.1-5.5-2.1-7.6,0c-2.1,2.1-2.1,5.5,0,7.6c0,0,0,0,0,0c2.1,2.1,5.5,2.1,7.6,0
C211.1,184.8,211.1,181.4,209,179.3C209,179.3,209,179.3,209,179.3z" />
<path class="st3" d="M200.3,188c-2.7-2.7-2.7-7.1,0-9.8c2.7-2.7,7.1-2.7,9.8,0c2.7,2.7,2.7,7.1,0,9.8
C207.4,190.7,203,190.7,200.3,188z M207.9,180.3c-1.5-1.5-4-1.5-5.5,0s-1.5,4,0,5.5c1.5,1.5,4,1.5,5.5,0
C209.4,184.3,209.4,181.9,207.9,180.3z" />
<path class="st3" d="M200.3,188l-34.5-34.5c-0.6-0.6-0.6-1.5,0-2.1s1.5-0.6,2.1,0l34.5,34.5c0.6,0.6,0.6,1.5,0,2.1
C201.8,188.6,200.9,188.6,200.3,188z M161.6,149.3c-0.3-0.3-0.5-0.7-0.4-1.1c0-0.4,0.1-0.8,0.4-1.1c0.3-0.3,0.7-0.5,1.1-0.4
c0.8,0,1.5,0.7,1.5,1.5c0,0.4-0.1,0.8-0.4,1.1c-0.3,0.3-0.7,0.5-1.1,0.4C162.2,149.7,161.9,149.6,161.6,149.3z" />
</g>
<g>
<path class="st2"
d="M107.9,26.1L95.6,13.7c-2.7-2.7-7-2.9-9.9-0.6l-9,7.1l24.7,24.7l7.1-8.9C110.9,33,110.6,28.7,107.9,26.1z" />
<path class="st2"
d="M107.9,26.1l-6.2-6.2L89,32.6l12.4,12.4l7.1-8.9C110.9,33,110.6,28.7,107.9,26.1z" />
<path class="st9" d="M78.7,18.1l24.7,24.7l-12,12L66.7,30.1L78.7,18.1z" />
<path class="st10" d="M91.1,30.4l12.4,12.4l-12,12L79.1,42.5L91.1,30.4z" />
<path class="st9" d="M76.6,20.2L76.6,20.2c-1.9-1.9-5.1-1.9-7.1,0l-6.4,6.4l7.1,7.1l6.4-6.4C78.6,25.3,78.6,22.1,76.6,20.2z
M95,58.4l6.4-6.4c1.9-1.9,1.9-5.1,0-7.1l0,0c-1.9-1.9-5.1-1.9-7.1,0l-6.4,6.4" />
<path class="st3" d="M69.2,34.7c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4c1.4-1.4,1.4-3.6,0-4.9c-1.4-1.4-3.6-1.4-4.9,0l-6.4,6.4
c-0.6,0.6-1.5,0.6-2.1,0c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4c2.5-2.5,6.6-2.5,9.2,0s2.5,6.6,0,9.2l-6.4,6.4
C70.7,35.3,69.8,35.3,69.2,34.7z M93.9,59.4c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4c1.4-1.4,1.4-3.6,0-4.9s-3.6-1.4-4.9,0L89,52.4
c-0.6,0.6-1.5,0.6-2.1,0c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4c2.5-2.5,6.6-2.5,9.2,0c2.5,2.5,2.5,6.6,0,9.2l-6.4,6.4
C95.5,60,94.5,60,93.9,59.4z" />
<path class="st3" d="M101.8,43.8c0,0-0.1-0.1-0.1-0.1C95,34.6,87,26.6,78,19.9c-0.6-0.5-0.8-1.4-0.3-2.1c0.5-0.6,1.4-0.8,2.1-0.3
c9.3,6.9,17.5,15.1,24.4,24.4c0.5,0.6,0.4,1.6-0.3,2.1C103.2,44.3,102.4,44.3,101.8,43.8z" />
<path class="st3" d="M75.6,21.2c0,0-0.1-0.1-0.1-0.1c-0.5-0.6-0.4-1.6,0.2-2.1l9-7.1c3.6-2.8,8.7-2.5,12,0.7L109,25
c3.3,3.3,3.5,8.4,0.7,12l-7.1,8.9c-0.5,0.6-1.4,0.7-2.1,0.2c-0.7-0.5-0.7-1.4-0.2-2.1l7.1-8.9c1.9-2.4,1.7-5.8-0.4-8L94.5,14.7
c-2.2-2.2-5.6-2.4-8-0.5l-9,7.1C76.9,21.9,76.1,21.8,75.6,21.2z M92.9,56.3l-4.9,4.9l-2.8-2.8l4.9-4.9 M68.1,31.5l-4.9,4.9
l-2.8-2.8l4.9-4.9" />
<path class="st3" d="M86.9,62.3L84,59.4c-0.6-0.6-0.6-1.5,0-2.1l4.9-4.9c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1l-3.9,3.9
l0.7,0.7l3.9-3.9c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1L89,62.3C88.4,62.9,87.5,62.9,86.9,62.3z M62.1,37.5l-2.8-2.8
c-0.6-0.6-0.6-1.5,0-2.1l4.9-4.9c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1l-3.9,3.9l0.7,0.7l3.9-3.9c0.6-0.6,1.5-0.6,2.1,0
c0.6,0.6,0.6,1.5,0,2.1l-4.9,4.9C63.6,38.1,62.7,38.1,62.1,37.5z" />
<path class="st3" d="M95,60.5L61.1,26.6c-0.6-0.6-0.6-1.5,0-2.1s1.5-0.6,2.1,0l33.9,33.9c0.6,0.6,0.6,1.5,0,2.1
C96.5,61.1,95.6,61.1,95,60.5z" />
<path class="st2" d="M84.2,43.3l-5.9-5.9c-0.6-0.6-0.6-1.5,0-2.1l3.5-3.5c2.2-2.2,5.8-2.2,8.1,0l0,0c2.2,2.2,2.2,5.8,0,8.1
l-3.5,3.5C85.7,43.9,84.8,43.9,84.2,43.3z" />
<path class="st3" d="M106.3,22.3l-1.1,1.1l-7.1-7.1l1.1-1.1l0,0c1.2-1.2,3.1-1.2,4.2,0l2.8,2.8C107.5,19.2,107.5,21.1,106.3,22.3
L106.3,22.3z" />
<path class="st2" d="M74.9,22c-1-1-2.6-1-3.5,0c-1,1-1,2.6,0,3.5c1,1,2.6,1,3.5,0C75.8,24.5,75.8,22.9,74.9,22z M99.6,46.7
c-1-1-2.6-1-3.5,0s-1,2.6,0,3.5s2.6,1,3.5,0S100.6,47.7,99.6,46.7z" />
<path class="st3" d="M89,64.4L59.4,34.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l29.6,29.6c0.6,0.6,0.6,1.5,0,2.1
C90.5,65,89.6,65,89,64.4z M96.4,67.6c-0.6-0.6-1.5-0.6-2.1,0c-0.6,0.6-0.6,1.5,0,2.1s1.5,0.6,2.1,0C97,69.1,97,68.2,96.4,67.6z" />
</g>
<g>
<path class="st2" d="M139.1,66.4l24,24c1,1,1,2.5,0,3.5l-14.1,14.1c-1,1-2.5,1-3.5,0l-24-24c-1-1-1-2.5,0-3.5l14.1-14.1
C136.5,65.4,138.1,65.4,139.1,66.4z M120.7,72.7l7.1-7.1l4.2,4.2l-7.1,7.1L120.7,72.7z" />
<path class="st5" d="M146.1,73.4l17,17c1,1,1,2.5,0,3.5l-14.1,14.1c-1,1-2.5,1-3.5,0l-17-17c-1-1-1-2.5,0-3.5l14.1-14.1
C143.6,72.4,145.1,72.4,146.1,73.4z" />
<path class="st6" d="M159.6,90.4L159.6,90.4c1-1,1-2.5,0-3.5l-13.4-13.4c-1-1-2.5-1-3.5,0l-1.8,1.8L156,90.4
C157,91.4,158.6,91.4,159.6,90.4z" />
<path class="st3" d="M164.2,95L150,109.1c-1.6,1.6-4.1,1.6-5.7,0l-24-24c-1.6-1.6-1.6-4.1,0-5.7l14.1-14.1c1.6-1.6,4.1-1.6,5.7,0
l24,24C165.7,90.9,165.7,93.4,164.2,95z M122.4,81.6c-0.4,0.4-0.4,1,0,1.4l24,24c0.4,0.4,1,0.4,1.4,0L162,92.9c0.4-0.4,0.4-1,0-1.4
l-24-24c-0.4-0.4-1-0.4-1.4,0L122.4,81.6z" />
<path class="st2" d="M148.2,103.8c-0.6,0.6-1.5,0.6-2.1,0l-8.8-8.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l8.8,8.8
C148.8,102.3,148.8,103.2,148.2,103.8z M134.1,89.7c-0.3,0.3-0.7,0.5-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4c-0.3-0.3-0.5-0.7-0.4-1.1
c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0.1-0.3s0.1-0.2,0.1-0.3c0.3-0.4,0.7-0.7,1.2-0.7c0.4,0,0.8,0.1,1.1,0.4c0.1,0.1,0.1,0.1,0.2,0.2
c0.1,0.1,0.1,0.2,0.1,0.3s0.1,0.2,0.1,0.3c0,0.1,0,0.2,0,0.3C134.6,89,134.4,89.4,134.1,89.7z" />
<path class="st3" d="M133,71c-0.6,0.6-1.5,0.6-2.1,0l-3.2-3.2l-4.9,4.9l0.4,0.4c0.6,0.6,0.6,1.5,0,2.1c-0.6,0.6-1.5,0.6-2.1,0
l-1.4-1.4c-0.6-0.6-0.6-1.5,0-2.1l7.1-7.1c0.6-0.6,1.5-0.6,2.1,0l4.2,4.2C133.6,69.4,133.6,70.4,133,71z" />
</g>
<g>
<path class="st1" d="M315,113.9l3,3l-1.9,1.9l7.6,7.6l3.7-3.7l-7.6-7.6l-1.8,1.8" />
<path class="st2"
d="M318.3,184.5l-37.7-37.7c-2.1-2.1-2.1-5.4,0-7.5l18.7-18.7c2-2,5.4-2,7.5,0l37.7,37.7" />
<path class="st2" d="M313.6,140.2l-13.4,13.4c-1,1-2.7,1-3.7,0l-8.7-8.7c-1-1-1-2.7,0-3.7l13.4-13.4c1-1,2.7-1,3.7,0l8.7,8.7
C314.6,137.5,314.6,139.2,313.6,140.2z" />
<path class="st3" d="M345.7,159.6c-0.7,0.7-1.9,0.7-2.6,0L305.5,122c-1.3-1.3-3.6-1.4-4.9,0l-18.7,18.7c-1.3,1.3-1.3,3.5,0,4.9
l37.7,37.7c0.7,0.7,0.7,1.9,0,2.6c-0.7,0.7-1.9,0.7-2.6,0l-37.7-37.7c-2.8-2.8-2.8-7.3,0-10.1l18.7-18.7c2.8-2.8,7.3-2.8,10.1,0
l37.7,37.7C346.5,157.8,346.4,158.9,345.7,159.6z" />
<path class="st4" d="M313.6,140.2l-13.4,13.4c-1,1-2.7,1-3.7,0l-8.7-8.7c-1-1-1-2.7,0-3.7l13.4-13.4c1-1,2.7-1,3.7,0l8.7,8.7
C314.6,137.5,314.6,139.2,313.6,140.2z" />
<path class="st3" d="M314.9,141.5l-13.4,13.4c-1.7,1.7-4.6,1.7-6.3,0l-8.7-8.7c-1.7-1.7-1.7-4.6,0-6.3l13.4-13.4
c1.7-1.7,4.6-1.7,6.3,0l8.7,8.7C316.6,136.9,316.6,139.7,314.9,141.5z M289.1,142.5c-0.3,0.3-0.3,0.8,0,1.1l8.7,8.7
c0.3,0.3,0.8,0.3,1.1,0l13.4-13.4c0.3-0.3,0.3-0.8,0-1.1l-8.7-8.7c-0.3-0.3-0.8-0.3-1.1,0L289.1,142.5z M349.5,155.9l-33.5,33.5
c-0.7,0.7-1.9,0.7-2.6,0c-0.7-0.7-0.7-1.9,0-2.6l33.5-33.5c0.7-0.7,1.9-0.7,2.6,0C350.2,154,350.2,155.2,349.5,155.9z M347.5,150.2
c-1.7,1.7-4.6,1.7-6.3,0l-15.1-15.1c-0.3-0.3-0.8-0.3-1.1,0l-1.9,1.9c-0.7,0.7-1.9,0.7-2.6,0c-0.7-0.7-0.7-1.9,0-2.6l1.9-1.9
c1.7-1.7,4.6-1.7,6.3,0l15.1,15.1c0.3,0.3,0.8,0.3,1.1,0c0.3-0.3,0.3-0.8,0-1.1l-20.7-20.7c-0.7-0.7-0.7-1.9,0-2.6s1.9-0.7,2.6,0
l20.7,20.7C349.2,145.6,349.2,148.5,347.5,150.2z" />
<path class="st3" d="M328.6,124l-3.7,3.7c-0.7,0.7-1.9,0.7-2.6,0l-7.6-7.6c-0.7-0.7-0.7-1.9,0-2.6l0.6-0.6l-1.7-1.7
c-0.7-0.7-0.7-1.9,0-2.6c0.7-0.7,1.9-0.7,2.6,0l1.7,1.7l0.5-0.5c0.7-0.7,1.9-0.7,2.6,0l7.5,7.5C329.3,122.1,329.3,123.3,328.6,124z
M323.6,123.8l1.1-1.1l-4.9-4.9l-1.1,1.1L323.6,123.8z" />
<g>
<path class="st5" d="M324.8,168.4c-0.2,0.2-0.5,0.4-0.9,0.5c-1,0.2-2-0.4-2.2-1.3l-1.3-5.7l-1.3,1.9c-0.4,0.6-1.1,0.9-1.8,0.7
c-0.7-0.2-1.3-0.7-1.4-1.4l-2.3-9.5c-0.2-1,0.4-2,1.3-2.2c1-0.2,2,0.4,2.2,1.3l1.3,5.7l1.3-1.9c0.4-0.6,1.1-0.9,1.8-0.7
c0.7,0.2,1.3,0.7,1.4,1.4l2.2,9.5C325.4,167.4,325.3,168,324.8,168.4z" />
</g>
</g>
<g>
<path class="st11"
d="M233.8,171.2l15.3,15.3l15.9-26.2c0.3-0.5,0.3-1.1-0.2-1.6l-3.3-3.3c-0.4-0.4-1-0.5-1.6-0.2L233.8,171.2z" />
<path class="st2"
d="M244.5,164.7l-6.2,3.8l13.5,13.5l0,0l3.8-6.2L244.5,164.7z M256.1,157.6l-6.2,3.8l9,9l3.8-6.2L256.1,157.6z" />
<path class="st12"
d="M263.6,157.4l1.3,1.3c0.4,0.4,0.5,1,0.2,1.6l-16,26.2l-3.5-3.5l18.2-24C264.1,158.4,264,157.8,263.6,157.4z" />
<g>
<path class="st3" d="M248.4,187.2l-15.3-15.3c-0.2-0.2-0.3-0.5-0.3-0.8c0-0.3,0.2-0.6,0.5-0.7l26.2-15.9c0.9-0.5,2-0.4,2.8,0.3
l3.3,3.3c0.7,0.7,0.9,1.9,0.3,2.8L249.9,187c-0.2,0.3-0.4,0.4-0.7,0.5C248.9,187.5,248.6,187.4,248.4,187.2z M235.4,171.4
l13.5,13.5l15.3-25.2c0.1-0.1,0.1-0.3,0-0.3l-3.3-3.3c-0.1-0.1-0.2-0.1-0.3,0L235.4,171.4z" />
<path class="st3" d="M230.5,169.3c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l20.4,20.4l0,0c0.4,0.4,0.4,1,0,1.4c-0.4,0.4-1,0.4-1.4,0
L230.5,169.3L230.5,169.3z" />
</g>
</g>
<g>
<path class="st2" d="M184,108.7L196.6,96c3.9-3.9,10.3-3.9,14.1,0l22.6,22.6c2.7,2.7,3.6,6.7,2.5,10.3l-0.4,1.2
c-2.2,6.4-6.2,12.1-11.5,16.4l-2,1.7c-1.1,0.9-2.5-0.7-1.5-1.7l0,0" />
<path class="st2" d="M226,144.4l0.1,0.1l0,0c1.2-1.2,2.4-2.6,3.5-3.9c0,0,0.1-0.1,0.1-0.1c0.2-0.2,0.4-0.5,0.5-0.7
c0.1-0.1,0.1-0.1,0.1-0.2c0.2-0.2,0.3-0.5,0.5-0.7c0.1-0.1,0.1-0.2,0.2-0.2c0.1-0.2,0.3-0.5,0.5-0.7c0.1-0.1,0.1-0.1,0.1-0.2
c0.3-0.5,0.6-1,0.9-1.4c0,0,0.1-0.1,0.1-0.2c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.2,0.2-0.3,0.3-0.5c0.1-0.2,0.2-0.3,0.2-0.5
c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.1,0.2-0.3,0.2-0.5c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.1,0.1-0.3,0.2-0.5c0.1-0.2,0.2-0.5,0.3-0.7
c0-0.1,0.1-0.2,0.1-0.4c0.1-0.4,0.2-0.7,0.4-1.1l0.4-1.2c1.2-3.6,0.2-7.6-2.5-10.3l-22.5-22.5c-3.9-3.9-10.3-3.9-14.1,0l-2.8,2.8
l30.8,30.8C228.5,133.6,229.1,139.7,226,144.4z" />
<path class="st2" d="M209.2,110.1l-3.5-3.5c-1-1-1-2.5,0-3.5l0.7-0.7c1-1,2.5-1,3.5,0l3.5,3.5c1,1,1,2.5,0,3.5l-0.7,0.7
C211.7,111.1,210.2,111,209.2,110.1z M218,118.9l-3.5-3.5c-1-1-1-2.5,0-3.5l0.7-0.7c1-1,2.5-1,3.5,0l3.5,3.5c1,1,1,2.5,0,3.5
l-0.7,0.7C220.6,119.9,219,119.9,218,118.9z M233.5,134.4c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.1,0.2-0.3,0.2-0.5
c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.1,0.1-0.3,0.2-0.5c0.1-0.2,0.2-0.5,0.3-0.7c0-0.1,0.1-0.2,0.1-0.4c0.1-0.4,0.2-0.7,0.4-1.1l0.4-1.2
c0.1-0.2,0.1-0.4,0.2-0.7l-8.2-8.2c-1-1-2.5-1-3.5,0l-0.8,0.8c-1,1-1,2.5,0,3.5L233.5,134.4L233.5,134.4z" />
<path class="st3" d="M219.3,149.2c0,0-0.1-0.1-0.1-0.1c-1-1-0.9-2.6,0.1-3.6c0.6-0.6,1.5-0.6,2.1,0c0.2,0.2,0.4,0.5,0.4,0.8
l1.1-0.9c5.1-4.1,8.8-9.6,11-15.8l0.4-1.2c1-3.1,0.2-6.4-2.1-8.7l-22.6-22.6c-3.3-3.3-8.7-3.3-12,0L185,109.7
c-0.6,0.6-1.5,0.6-2.1,0c-0.6-0.6-0.6-1.5,0-2.1L195.5,95c4.5-4.5,11.8-4.5,16.3,0l22.6,22.6c3.1,3.1,4.2,7.6,2.8,11.8l-0.4,1.2
c-2.3,6.7-6.4,12.6-11.9,17.1l-2,1.7C221.7,150.2,220.3,150.1,219.3,149.2z M226.7,148.6c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4
c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1l-6.4,6.4C228.3,149.2,227.3,149.2,226.7,148.6z" />
<path class="st3" d="M210.8,139.1l-16.5-16.5c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0L213,137c0.6,0.6,0.6,1.5,0,2.1
C212.4,139.7,211.4,139.7,210.8,139.1z M186.8,115l-5.4-5.4c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l5.4,5.4
c0.6,0.6,0.6,1.5,0,2.1C188.3,115.6,187.4,115.6,186.8,115z M236.2,117.3l-23.4-23.4c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0
l23.4,23.4c0.6,0.6,0.6,1.5,0,2.1C237.7,117.9,236.8,117.9,236.2,117.3z M225.1,145.6c-0.6-0.6-0.6-1.4-0.1-2.1
c2.7-3.1,1.9-9.3-1.6-12.8l-30.8-30.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l30.8,30.8c4.6,4.6,5.4,12.6,1.7,16.9
C226.7,146.1,225.8,146.2,225.1,145.6C225.1,145.6,225.1,145.6,225.1,145.6z M198.7,98.9l-2.2-2.2c-0.6-0.6-0.6-1.5,0-2.1
s1.5-0.6,2.1,0l2.2,2.2c0.6,0.6,0.6,1.5,0,2.1C200.3,99.5,199.3,99.5,198.7,98.9z M203,97.5l-2.2-2.2c-0.6-0.6-0.6-1.5,0-2.1
c0.6-0.6,1.5-0.6,2.1,0l2.2,2.2c0.6,0.6,0.6,1.5,0,2.1C204.5,98.1,203.6,98.1,203,97.5z" />
<path class="st2" d="M219.2,138.5c-1.9-1.9-4.9-1.9-6.8,0c-1.9,1.9-1.9,4.9,0,6.8c0,0,0,0,0,0c1.9,1.9,4.9,1.9,6.8,0
C221,143.4,221,140.4,219.2,138.5C219.2,138.5,219.2,138.5,219.2,138.5z" />
<path class="st3" d="M211.3,146.4c-2.5-2.5-2.4-6.5,0-8.9c2.4-2.4,6.5-2.4,8.9,0c2.4,2.4,2.5,6.5,0,8.9
C217.8,148.9,213.8,148.8,211.3,146.4z M218.1,139.6c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0
C219.4,143,219.4,140.9,218.1,139.6z" />
<path class="st2" d="M188.2,114.3c-1.9,1.9-1.9,4.9,0,6.8c1.9,1.9,4.9,1.9,6.8,0c0.8-0.8,1.3-1.8,1.4-2.9l-5.3-5.3
C190,113.1,189,113.5,188.2,114.3z" />
<path class="st3" d="M187.2,122.2c-2.5-2.5-2.4-6.5,0-8.9c1-1,2.3-1.6,3.8-1.8c0.5,0,0.9,0.1,1.2,0.4l5.3,5.3
c0.3,0.3,0.5,0.8,0.4,1.2c-0.1,1.4-0.8,2.8-1.8,3.8C193.6,124.7,189.6,124.6,187.2,122.2z M190.6,114.6c-0.5,0.1-0.9,0.4-1.3,0.8
c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0c0.4-0.4,0.7-0.8,0.8-1.3L190.6,114.6z" />
<g>
<path class="st3" d="M217.7,98.1c-0.2-0.2-0.4-0.6-0.4-1l-0.2-5.2c0-0.8,0.6-1.5,1.4-1.6c0.8,0,1.5,0.6,1.6,1.4l0.2,5.2
c0,0.8-0.6,1.5-1.4,1.6C218.4,98.5,218,98.3,217.7,98.1z" />
</g>
<g>
<path class="st3" d="M219.3,147.6l-0.9-0.9c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l0.9,0.9c0.6,0.6,0.6,1.5,0,2.1
C220.8,148.2,219.9,148.2,219.3,147.6z" />
</g>
</g>
<g>
<path class="st2" d="M194.9,72.9l-1.1,1.1l-35.4-35.4l25.8-25.8c1.9-1.9,5.1-1.9,7.1,0l24.7,24.7l0,0
C214.9,47.8,202.2,65.6,194.9,72.9z" />
<path class="st5" d="M181,34.4l16.9-4.1c0.6-0.1,0.9,0.6,0.4,0.9l-10,6.6l3.4,3.4c0.3,0.3,0.1,0.8-0.3,0.8L176,44.4
c-0.5,0.1-0.8-0.6-0.4-0.9l8.3-5.1l-3.3-3.3C180.5,34.9,180.6,34.5,181,34.4z" />
<path class="st3" d="M209.4,52.7l-5.3-5.3c-2.5-2.5-2.5-6.6,0-9.2l2.8-2.8c0.6-0.6,1.5-0.6,2.1,0s0.6,1.5,0,2.1l-2.8,2.8
c-1.4,1.4-1.4,3.6,0,4.9l5.3,5.3c0.6,0.6,0.6,1.5,0,2.1C210.9,53.3,210,53.3,209.4,52.7z" />
<path class="st3" d="M192.8,75l-35.4-35.4c-0.6-0.6-0.6-1.5,0-2.1l25.8-25.8c2.5-2.5,6.6-2.5,9.2,0l24.7,24.7c0,0,0,0,0,0l2.8,2.8
c0.6,0.6,0.6,1.5,0,2.1c-0.6,0.6-1.5,0.6-2.1,0l-0.8-0.8c-2.9,11-14.6,26.8-21.1,33.3l-1.1,1.1C194.3,75.6,193.3,75.6,192.8,75z
M160.6,38.6l33.2,33.2c7.6-7.6,19.2-24.3,20.7-33.8l-24.2-24.2c-1.4-1.4-3.6-1.4-4.9,0C185.3,13.9,160.6,38.6,160.6,38.6z" />
<path class="st3" d="M192.8,75L156,38.2c-0.6-0.6-0.6-1.5,0-2.1s1.5-0.6,2.1,0l36.8,36.8c0.6,0.6,0.6,1.5,0,2.1
C194.3,75.6,193.3,75.6,192.8,75z" />
<path class="st2" d="M166,42.2c-1.9-1.9-4.9-1.9-6.8,0c-1.9,1.9-1.9,4.9,0,6.8c0,0,0,0,0,0c1.9,1.9,4.9,1.9,6.8,0
C167.9,47.2,167.9,44.1,166,42.2C166,42.2,166,42.2,166,42.2z M188.6,64.9c-1.9-1.9-4.9-1.9-6.8,0c-1.9,1.9-1.9,4.9,0,6.8
c0,0,0,0,0,0c1.9,1.9,4.9,1.9,6.8,0C190.5,69.8,190.5,66.7,188.6,64.9C188.6,64.9,188.6,64.9,188.6,64.9z" />
<path class="st3" d="M158.2,50.1c-2.5-2.5-2.4-6.5,0-8.9s6.5-2.4,8.9,0c2.4,2.4,2.4,6.5,0,8.9C164.6,52.5,160.6,52.6,158.2,50.1z
M164.9,43.3c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0C166.2,46.7,166.3,44.6,164.9,43.3z" />
<g>
<path class="st3" d="M180.8,72.7c-2.5-2.5-2.4-6.5,0-8.9c2.4-2.4,6.5-2.4,8.9,0c2.5,2.5,2.4,6.5,0,8.9
C187.3,75.2,183.3,75.2,180.8,72.7z M187.6,65.9c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7s3.4,1.3,4.7,0
C188.8,69.3,188.9,67.2,187.6,65.9z" />
</g>
<g>
<path class="st3" d="M180.8,72.7L151.1,43c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l29.7,29.7c0.6,0.6,0.6,1.5,0,2.1
C182.3,73.3,181.4,73.3,180.8,72.7z" />
</g>
<g>
<path class="st3" d="M146.1,38.1c-0.1-0.1-0.1-0.1-0.2-0.2c0-0.1-0.1-0.2-0.1-0.3c0-0.1-0.1-0.2-0.1-0.3c0-0.1,0-0.2,0-0.3
c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0.1-0.3c0.1,0,0.1-0.1,0.2-0.2c0.1-0.1,0.1-0.2,0.2-0.2s0.1-0.1,0.2-0.2c0.1-0.1,0.2-0.1,0.3-0.1
s0.2,0,0.3-0.1s0.2,0,0.3,0c0.1,0,0.2,0,0.3,0c0.1,0,0.2,0,0.3,0.1s0.2,0.1,0.3,0.1c0.2,0.1,0.3,0.2,0.4,0.4
c0.1,0.1,0.1,0.2,0.1,0.3c0,0.1,0.1,0.2,0.1,0.3c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2-0.1,0.3c0,0.1-0.1,0.2-0.1,0.3
c-0.1,0.1-0.1,0.2-0.2,0.2s-0.1,0.1-0.2,0.2c-0.1,0.1-0.2,0.1-0.3,0.1s-0.2,0.1-0.3,0.1c-0.1,0-0.2,0-0.3,0c-0.1,0-0.2,0-0.3,0
c-0.1,0-0.2,0-0.3-0.1c-0.1,0-0.2-0.1-0.3-0.1C146.3,38.3,146.2,38.1,146.1,38.1z" />
</g>
</g>
<path class="st3"
d="M30.2,31.2l3.1,3.1l0,0c0,0,0,0,0,0c-2,1.9-3.9,3.4-6.2,1.8L26.8,36l-0.6-0.6" />
<path class="st3" d="M32.6,35L29.5,32c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l3.1,3.1c0.4,0.4,0.4,1,0,1.4S33,35.4,32.6,35z M37.7,23.8
l3.1,3.1l0,0c0,0,0,0,0,0c-2,1.9-3.9,3.4-6.2,1.8l-0.3-0.2l-0.6-0.6" />
<path class="st3" d="M40,27.6L37,24.5c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l3.1,3.1c0.4,0.4,0.4,1,0,1.4S40.5,28,40,27.6z M20.8,21.8
l-3.1-3.1l0,0c0,0,0,0,0,0c-1.9,2-3.4,3.9-1.8,6.2l0.2,0.3l0.6,0.6" />
<path class="st3" d="M20.1,22.5L17,19.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l3.1,3.1c0.4,0.4,0.4,1,0,1.4S20.4,22.9,20.1,22.5z
M28.2,14.3l-3.1-3.1l0,0c0,0,0,0,0,0c-1.9,2-3.4,3.9-1.8,6.2l0.2,0.3l0.6,0.6" />
<path class="st3"
d="M27.5,15L24.4,12c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l3.1,3.1c0.4,0.4,0.4,1,0,1.4C28.6,15.5,27.9,15.4,27.5,15z" />
<g>
<path class="st2" d="M20.3,36.7L15.7,32c-1.3-1.3-1.3-3.5,0-4.8l13.2-13.2c1.3-1.3,3.5-1.3,4.8,0l4.7,4.7c1.3,1.3,1.3,3.5,0,4.8
L25.1,36.7C23.8,38,21.7,38,20.3,36.7z" />
</g>
<g>
<path class="st11" d="M33.6,18.5c-1.5-1.5-3.9-1.5-5.3,0c-1.5,1.5-1.5,3.9,0,5.3c0,0,0,0,0,0c1.5,1.5,3.9,1.5,5.3,0
S35,19.9,33.6,18.5C33.6,18.5,33.6,18.5,33.6,18.5z" />
</g>
<g>
<path class="st2" d="M26,26c-1.5-1.5-3.9-1.5-5.3,0s-1.5,3.9,0,5.3c0,0,0,0,0,0c1.5,1.5,3.9,1.5,5.3,0C27.5,29.9,27.5,27.5,26,26
C26,26,26,26,26,26z" />
</g>
<g>
<path class="st3" d="M19.6,37.4l-4.7-4.7c-1.7-1.7-1.7-4.5,0-6.2l13.2-13.2c1.7-1.7,4.5-1.7,6.2,0l4.7,4.7c1.7,1.7,1.7,4.5,0,6.2
L25.9,37.4C24.1,39.1,21.4,39.1,19.6,37.4z M33,14.7c-0.9-0.9-2.4-0.9-3.4,0L16.4,27.9c-0.9,0.9-0.9,2.4,0,3.4l4.7,4.7
c0.9,0.9,2.4,0.9,3.4,0l13.2-13.2c0.9-0.9,0.9-2.4,0-3.4L33,14.7z" />
</g>
<g>
<path class="st3" d="M10.3,41.7c-0.4-0.4-0.4-1,0-1.4l6.7-6.7c0.4-0.4,1-0.4,1.4,0c0.4,0.4,0.4,1,0,1.4l-6.7,6.7
C11.4,42.1,10.7,42.1,10.3,41.7z" />
</g>
<g>
<path class="st5"
d="M282.1,91.1l-24.8-24.8l-16.5,16.5c-1.5,1.5-1.5,4,0,5.5l19.3,19.3c1.5,1.5,4,1.5,5.5,0L282.1,91.1z" />
<path class="st2" d="M257,85.2l11.7-2.9c0.4-0.1,0.6,0.4,0.3,0.6l-6.9,4.6l2.4,2.4c0.2,0.2,0.1,0.5-0.2,0.6l-10.6,1.7
c-0.4,0.1-0.5-0.4-0.2-0.6l5.7-3.4l-2.3-2.3C256.6,85.6,256.7,85.3,257,85.2z" />
<path class="st6" d="M279.4,88.4l-16,16c-0.3,0.3-0.8,0.3-1.1,0l-11.8-11.8c-0.8-0.8-2-0.8-2.8,0l0,0c-0.8,0.8-0.8,2,0,2.8
l12.4,12.4c1.5,1.5,4,1.5,5.5,0l16.5-16.5L279.4,88.4z" />
<path class="st2" d="M259.3,61.7l27.5,27.5l-3.3,3.3L256,65L259.3,61.7z M267.5,69.9l3.3-3.3l-4.1-4.1l-3.3,3.3 M282.7,85.1
l3.3-3.3l-4.1-4.1l-3.3,3.3" />
<path class="st3" d="M259.3,108.5L240,89.2c-2-2-2-5.2,0-7.2l13.8-13.8c0.5-0.5,1.2-0.5,1.7,0c0.5,0.5,0.5,1.2,0,1.7l-13.8,13.8
c-1.1,1.1-1.1,2.8,0,3.9l19.3,19.3c1.1,1.1,2.8,1.1,3.9,0l16.5-16.5c0.5-0.5,1.2-0.5,1.7,0c0.5,0.5,0.5,1.2,0,1.7l-16.5,16.5
C264.4,110.4,261.3,110.4,259.3,108.5z" />
<path class="st2" d="M251,78.2c-0.5-0.5-0.5-1.2,0-1.7l8.3-8.3c0.5-0.5,1.2-0.5,1.7,0c0.5,0.5,0.5,1.2,0,1.7l-8.3,8.3
C252.2,78.6,251.5,78.6,251,78.2z M246.9,82.3c-0.1-0.1-0.1-0.1-0.1-0.2c-0.1-0.1-0.1-0.1-0.1-0.2c0-0.1,0-0.1-0.1-0.2
c0-0.1,0-0.1,0-0.2c0-0.1,0-0.2,0-0.2c0-0.1,0-0.1,0.1-0.2c0-0.1,0.1-0.1,0.1-0.2c0-0.1,0.1,0,0.1-0.1c0.1-0.1,0.1-0.1,0.2-0.1
c0.1,0,0.1-0.1,0.2-0.1s0.2-0.1,0.2-0.1c0.1,0,0.1,0,0.2,0c0.1,0,0.2,0,0.2,0s0.1,0,0.2,0.1c0.1,0,0.1,0.1,0.2,0.1
c0.3,0.2,0.5,0.6,0.5,1c0,0.1,0,0.2,0,0.2c0,0.1,0,0.1-0.1,0.2c0,0.1-0.1,0.1-0.1,0.2s-0.2,0.1-0.2,0.1c-0.2,0.2-0.5,0.4-0.8,0.3
C247.4,82.7,247.1,82.5,246.9,82.3z" />
<path class="st3" d="M282.7,93.3l-27.5-27.5c-0.5-0.5-0.5-1.2,0-1.7l3.3-3.3c0.5-0.5,1.2-0.5,1.7,0l27.5,27.5
c0.5,0.5,0.5,1.2,0,1.7l-3.3,3.3C283.9,93.8,283.1,93.8,282.7,93.3z M257.6,65l25.9,25.9l1.7-1.7l-25.9-25.9L257.6,65z" />
<path class="st3" d="M266.7,70.7c-0.5-0.5-0.5-1.2,0-1.7l2.5-2.5l-2.5-2.5l-0.3,0.3c-0.5,0.5-1.2,0.5-1.7,0c-0.5-0.5-0.5-1.2,0-1.7
l1.1-1.1c0.5-0.5,1.2-0.5,1.7,0l4.1,4.1c0.5,0.5,0.5,1.2,0,1.7l-3.3,3.3C267.9,71.2,267.2,71.2,266.7,70.7z M281.8,85.9
c-0.5-0.5-0.5-1.2,0-1.7l2.5-2.5l-2.5-2.5l-0.3,0.3c-0.5,0.5-1.2,0.5-1.7,0c-0.5-0.5-0.5-1.2,0-1.7l1.1-1.1c0.5-0.5,1.2-0.5,1.7,0
l4.1,4.1c0.5,0.5,0.5,1.2,0,1.7l-3.3,3.3C283,86.3,282.3,86.3,281.8,85.9z" />
</g>
<g>
<path class="st7" d="M308.8,72.2l34.3,34.3l3.8-3.8c2.1-2.1,2.1-5.5,0-7.6l-13.6-13.6l-0.6-0.2c-2.3-0.8-3-3.7-1.3-5.5l-1.2-1.2
C324,68.5,316.8,68.8,308.8,72.2" />
<path class="st8" d="M319.7,69.9C319.7,69.8,319.7,69.8,319.7,69.9c-0.4-0.1-0.7-0.1-1-0.1c-3.1,0.1-6.4,1-9.9,2.4l0,0l10.9,10.9
C323.3,79.5,323.3,73.5,319.7,69.9 M348.4,98.8c-3.6-2.7-8.8-2.4-12.2,0.9l6.8,6.8l3.8-3.8C347.9,101.6,348.4,100.2,348.4,98.8" />
<path class="st3" d="M342,107.6l-34.3-34.3c-0.4-0.4-0.5-0.8-0.4-1.3c0.1-0.5,0.4-0.9,0.9-1.1c9.8-4.2,17.1-3.3,23.1,2.8l0.1,0.1
c0.6,0.6,0.6,1.5,0,2.1c-0.6,0.6-1.5,0.6-2.1,0l-0.1-0.1c-4.7-4.7-10.3-5.7-17.8-2.9l31.6,31.6l2.8-2.8c1.5-1.5,1.5-4,0-5.5
L333.7,84c-0.4-0.4-0.5-0.8-0.4-1.3l0.6-3.8c0.1-0.8,0.9-1.4,1.7-1.2c0.8,0.1,1.4,0.9,1.2,1.7l-0.5,3L348,94c2.7,2.7,2.7,7.1,0,9.8
l-3.8,3.8C343.6,108.2,342.6,108.2,342,107.6z" />
<path class="st2" d="M316.9,72.7c-2.1-2.1-5.5-2.1-7.6,0c-2.1,2.1-2.1,5.5,0,7.6c0,0,0,0,0,0c2.1,2.1,5.5,2.1,7.6,0
C319,78.2,319,74.8,316.9,72.7C316.9,72.7,316.9,72.7,316.9,72.7z M336.7,88.2c-1.2-1.2-3.1-1.2-4.2,0c-1.2,1.2-1.2,3.1,0,4.2
s3.1,1.2,4.2,0C337.8,91.2,337.8,89.3,336.7,88.2z" />
<path class="st3" d="M308.2,81.4c-2.7-2.7-2.7-7.1,0-9.8s7.1-2.7,9.8,0s2.7,7.1,0,9.8C315.2,84.1,310.9,84.1,308.2,81.4z
M315.8,73.7c-1.5-1.5-4-1.5-5.5,0c-1.5,1.5-1.5,4,0,5.5c1.5,1.5,4,1.5,5.5,0C317.3,77.7,317.3,75.3,315.8,73.7z" />
<path class="st2" d="M346.7,102.5c-2.1-2.1-5.5-2.1-7.6,0c-2.1,2.1-2.1,5.5,0,7.6c0,0,0,0,0,0c2.1,2.1,5.5,2.1,7.6,0
S348.8,104.6,346.7,102.5C346.7,102.5,346.7,102.5,346.7,102.5z" />
<path class="st3" d="M338,111.2c-2.7-2.7-2.7-7.1,0-9.8s7.1-2.7,9.8,0c2.7,2.7,2.7,7.1,0,9.8C345.1,113.9,340.7,113.9,338,111.2z
M345.7,103.6c-1.5-1.5-4-1.5-5.5,0c-1.5,1.5-1.5,4,0,5.5c1.5,1.5,4,1.5,5.5,0C347.2,107.6,347.2,105.1,345.7,103.6z" />
<path class="st3" d="M338,111.2l-34.5-34.5c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l34.5,34.5c0.6,0.6,0.6,1.5,0,2.1
C339.5,111.8,338.6,111.8,338,111.2z M299.3,72.5c-0.3-0.3-0.5-0.7-0.4-1.1c0-0.4,0.1-0.8,0.4-1.1s0.7-0.5,1.1-0.4
c0.8,0,1.5,0.7,1.5,1.5c0,0.4-0.1,0.8-0.4,1.1c-0.3,0.3-0.7,0.5-1.1,0.4C300,73,299.6,72.8,299.3,72.5z" />
</g>
<g>
<path class="st3" d="M277.7,28.4c0-3,2.4-5.4,5.4-5.4s5.4,2.4,5.4,5.4s-2.4,5.4-5.4,5.4 M264.6,15.2c0-3,2.4-5.4,5.4-5.4
c3,0,5.4,2.4,5.4,5.4s-2.4,5.4-5.4,5.4" />
<path class="st11" d="M275,44.3l-21-21c-0.2-0.2-0.2-0.6,0-0.9l6.8-6.8c0.2-0.2,0.6-0.2,0.9,0l21,21c0.2,0.2,0.2,0.6,0,0.9
l-6.8,6.8C275.6,44.6,275.2,44.6,275,44.3z" />
<path class="st2" d="M255.1,21.4l-1.1,1.1c-0.2,0.2-0.2,0.6,0,0.9l1.8,1.8L270,24l0,0l-3.8-3.8L255.1,21.4z M259.2,28.6l-0.2-0.2
l4,4l14.2-1.1l0,0l-3.8-3.8L259.2,28.6z M281.6,38.7l1.1-1.1c0.2-0.2,0.2-0.6,0-0.9l-1.8-1.8L266.7,36l0,0l3.7,3.7L281.6,38.7z
M271.6,13.6c-0.9-0.9-2.4-0.9-3.3,0s-0.9,2.4,0,3.3c0.9,0.9,2.4,0.9,3.3,0C272.5,16,272.5,14.5,271.6,13.6z M284.8,26.7
c-0.9-0.9-2.4-0.9-3.3,0s-0.9,2.4,0,3.3s2.4,0.9,3.3,0S285.7,27.6,284.8,26.7z" />
<path class="st3" d="M274.3,45l-21-21c-0.6-0.6-0.6-1.6,0-2.2l6.8-6.8c0.6-0.6,1.6-0.6,2.2,0l21,21c0.6,0.6,0.6,1.6,0,2.2l-6.8,6.8
C275.9,45.6,275,45.6,274.3,45z M254.9,22.9l20.6,20.6l6.3-6.3l-20.6-20.6L254.9,22.9z M250.3,34.9c-0.4-0.4-0.4-0.9,0-1.3l5-5
c0.4-0.4,0.9-0.4,1.3,0c0.4,0.4,0.4,0.9,0,1.3l-5,5C251.2,35.3,250.7,35.3,250.3,34.9z M263.4,48.1c-0.4-0.4-0.4-0.9,0-1.3l5-5
c0.4-0.4,0.9-0.4,1.3,0c0.4,0.4,0.4,0.9,0,1.3l-5,5C264.4,48.4,263.8,48.4,263.4,48.1z" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 34 KiB

-14
View File
@@ -1,14 +0,0 @@
<svg id="Ebene_5" data-name="Ebene 5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<defs>
<style>.cls-1{fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:2px;}</style>
</defs>
<title>connector_ccs</title>
<path class="cls-1"
d="M20,8.2a7.89,7.89,0,0,1-8,7.87A7.91,7.91,0,0,1,6.29,2.6H17.71A8,8,0,0,1,20,8.2Z" />
<circle cx="12" cy="8.4" r="1.6" />
<circle cx="9.6" cy="5" r="1" />
<circle cx="14.4" cy="5" r="1" />
<circle cx="9" cy="18.73" r="1.37" />
<circle cx="15" cy="18.73" r="1.37" />
<rect class="cls-1" x="6.25" y="16.07" width="11.51" height="5.33" rx="2.67" />
</svg>

Before

Width:  |  Height:  |  Size: 651 B

-13
View File
@@ -1,13 +0,0 @@
<svg id="Ebene_5" data-name="Ebene 5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<defs>
<style>.cls-1,.cls-2{fill:none;}.cls-2{stroke:#000;stroke-miterlimit:10;stroke-width:2px;}
</style>
</defs>
<title>connector_cee_blau</title>
<path class="cls-1" d="M12,12H36V36H12Z" />
<circle cx="16" cy="10" r="2" />
<circle cx="8" cy="10" r="2" />
<circle cx="12" cy="17" r="1.25" />
<circle class="cls-2" cx="12" cy="12" r="9.5" />
<circle cx="12" cy="22" r="1.5" />
</svg>

Before

Width:  |  Height:  |  Size: 530 B

-15
View File
@@ -1,15 +0,0 @@
<svg id="Ebene_5" data-name="Ebene 5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<defs>
<style>.cls-1,.cls-2{fill:none;}.cls-2{stroke:#000;stroke-miterlimit:10;stroke-width:2px;}
</style>
</defs>
<title>connector_cee_rot</title>
<path class="cls-1" d="M12,12H36V36H12Z" />
<circle cx="16" cy="8" r="2" />
<circle cx="17" cy="14.5" r="2" />
<circle cx="7" cy="14.5" r="2" />
<circle cx="8" cy="8" r="2" />
<circle cx="12" cy="18" r="1.25" />
<circle class="cls-2" cx="12" cy="12" r="9.5" />
<circle cx="12" cy="22" r="1.5" />
</svg>

Before

Width:  |  Height:  |  Size: 604 B

-22
View File
@@ -1,22 +0,0 @@
<svg id="Ebene_5" data-name="Ebene 5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<defs>
<style>
.cls-1,.cls-2,.cls-3,.cls-4{fill:none;}.cls-2,.cls-3,.cls-4{stroke:#000;stroke-miterlimit:10;}.cls-3{stroke-width:2px;}.cls-4{stroke-width:0.5px;}
</style>
</defs>
<title>connector_chademo</title>
<path class="cls-1" d="M12,12H36V36H12Z" />
<circle class="cls-2" cx="17" cy="12" r="2" />
<path class="cls-3" d="M21,12A9,9,0,0,1,3,12a9.46,9.46,0,0,1,.06-1.07A9,9,0,0,1,21,12Z" />
<rect x="18.05" y="3.61" width="1.69" height="2"
transform="translate(2.27 14.71) rotate(-45)" />
<rect x="4.27" y="3.61" width="1.69" height="2"
transform="translate(5.46 11.49) rotate(-135)" />
<circle class="cls-2" cx="7" cy="12" r="2" />
<circle class="cls-2" cx="12" cy="7" r="2" />
<line class="cls-4" x1="10.5" y1="5.5" x2="13.5" y2="8.5" />
<line class="cls-4" x1="10.5" y1="8.5" x2="13.5" y2="5.5" />
<circle class="cls-2" cx="12" cy="17" r="2" />
<line class="cls-4" x1="10.5" y1="15.5" x2="13.5" y2="18.5" />
<line class="cls-4" x1="10.5" y1="18.5" x2="13.5" y2="15.5" />
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

-13
View File
@@ -1,13 +0,0 @@
<svg id="Ebene_5" data-name="Ebene 5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<defs>
<style>.cls-1,.cls-2{fill:none;}.cls-2{stroke:#000;stroke-miterlimit:10;stroke-width:2px;}
</style>
</defs>
<title>schuko</title>
<path class="cls-1" d="M12,12H36V36H12Z" />
<circle cx="16.5" cy="12" r="2" />
<circle cx="7.5" cy="12" r="2" />
<path class="cls-2" d="M21,12A9,9,0,0,1,3,12a9.46,9.46,0,0,1,.06-1.07A9,9,0,0,1,21,12Z" />
<rect x="11" y="3" width="2" height="2" />
<rect x="11" y="19" width="2" height="2" />
</svg>

Before

Width:  |  Height:  |  Size: 580 B

@@ -1,23 +0,0 @@
<svg id="Ebene_5" data-name="Ebene 5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<defs>
<style>.cls-1,.cls-2{fill:none;}.cls-2{stroke:#000;stroke-miterlimit:10;stroke-width:2px;}
</style>
</defs>
<title>connector_supercharger</title>
<path class="cls-1" d="M12,12H36V36H12Z" />
<path class="cls-2"
d="M13.45,17.08a8.24,8.24,0,0,1-3.11.6,8.34,8.34,0,0,1-6-14.18H16.3a8.35,8.35,0,0,1,1.07,10.33" />
<circle cx="10.34" cy="9.34" r="1.67" />
<circle cx="15.35" cy="9.34" r="1.67" />
<circle cx="12.84" cy="13.51" r="1.67" />
<circle cx="7.84" cy="13.51" r="1.67" />
<circle cx="5.34" cy="9.34" r="1.67" />
<circle cx="7.84" cy="5.59" r="1" />
<circle cx="12.84" cy="5.59" r="1.04" />
<g id="T">
<path id="path35"
d="M18.18,22.23l1-5.48c.93,0,1.22.1,1.27.52a2.15,2.15,0,0,0,.93-.7,6.91,6.91,0,0,0-2.46-.6l-.71.88h0L17.46,16a7,7,0,0,0-2.46.6,2.22,2.22,0,0,0,.94.7c0-.42.33-.52,1.26-.52l1,5.48" />
<path id="path37"
d="M18.18,15.72a7.9,7.9,0,0,1,3.28.66,2.65,2.65,0,0,0,.2-.4,9.24,9.24,0,0,0-7,0,2.61,2.61,0,0,0,.19.4,7.94,7.94,0,0,1,3.29-.66h0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

-19
View File
@@ -1,19 +0,0 @@
<svg id="Ebene_5" data-name="Ebene 5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<defs>
<style>
.cls-1,.cls-2,.cls-3{fill:none;}.cls-2,.cls-3{stroke:#000;stroke-miterlimit:10;}.cls-2{stroke-width:2px;}.cls-3{stroke-width:0.5px;}
</style>
</defs>
<title>connector_typ1</title>
<path class="cls-1" d="M12,12H36V36H12Z" />
<circle cx="15.79" cy="8.26" r="1.89" />
<circle cx="16.74" cy="14" r="1.18" />
<circle cx="7.26" cy="14" r="1.18" />
<circle cx="8.21" cy="8.26" r="1.89" />
<circle cx="12" cy="17.74" r="1.89" />
<circle class="cls-2" cx="12" cy="12.05" r="9" />
<rect x="10.58" y="21.05" width="2.84" height="1.89" />
<line class="cls-3" x1="10.5" y1="1" x2="13.5" y2="1" />
<polygon points="13.5 0.4 13.5 2.5 15.5 3.5 14.5 0.5 13.5 0.4" />
<polygon points="10.5 0.4 10.5 2.5 8.5 3.5 9.5 0.5 10.5 0.4" />
</svg>

Before

Width:  |  Height:  |  Size: 913 B

-16
View File
@@ -1,16 +0,0 @@
<svg id="Ebene_5" data-name="Ebene 5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<defs>
<style>.cls-1,.cls-2{fill:none;}.cls-2{stroke:#000;stroke-miterlimit:10;stroke-width:2px;}
</style>
</defs>
<title>connector_typ2</title>
<path class="cls-1" d="M12,12H36V36H12Z" />
<path class="cls-2" d="M22,10.5a10,10,0,1,1-17.14-7H19.14A10,10,0,0,1,22,10.5Z" />
<circle cx="12" cy="10.5" r="2" />
<circle cx="18" cy="10.5" r="2" />
<circle cx="15" cy="15.5" r="2" />
<circle cx="9" cy="15.5" r="2" />
<circle cx="6" cy="10.5" r="2" />
<circle cx="9" cy="6" r="1.25" />
<circle cx="15" cy="6" r="1.25" />
</svg>

Before

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

-473
View File
@@ -1,473 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 1024 500"
style="enable-background:new 0 0 1024 500;" xml:space="preserve">
<style type="text/css">
.st0{fill:#E6E6E6;}
.st1{fill:#DCDCDC;}
.st2{fill:#FFFFFF;}
.st3{fill:#808080;}
.st4{fill:#E8E8E8;}
.st5{fill:#99F5C8;}
.st6{opacity:0.26;fill:#1D1D1B;enable-background:new ;}
.st7{fill:#C8C8C8;}
.st8{fill:#B5B5B5;}
.st9{fill:#878787;}
.st10{fill:#666666;}
.st11{fill:#D1D1D1;}
.st12{opacity:0.2;fill:#808080;enable-background:new ;}
.st13{opacity:0.5;}
.st14{fill:#FFB300;}
.st15{fill:#90A4AE;}
.st16{fill:#546E7A;}
.st17{fill:#00E676;}
.st18{fill:#FFFFFF;fill-opacity:0.2;}
.st19{fill:#3E2723;fill-opacity:0.2;}
.st20{opacity:0.45;enable-background:new ;}
.st21{font-family:'Roboto-Light';}
.st22{font-size:136.5333px;}
</style>
<g id="Ebene_1">
<rect y="-34.4" class="st0" width="1024" height="568.9" />
<g>
<path class="st1"
d="M135.7,135.4l8.5,8.5l-5.4,5.4l21.6,21.6l10.5-10.5l-21.6-21.6l-5.1,5.1" />
<path class="st2"
d="M145.4,335.9L38.1,228.7c-6-6-6-15.4,0-21.3L91,154.1c5.7-5.7,15.4-5.7,21.3,0l107.2,107.2" />
<path class="st2" d="M131.7,209.9L93.6,248c-2.8,2.8-7.7,2.8-10.5,0l-24.7-24.7c-2.8-2.8-2.8-7.7,0-10.5l38.1-38.1
c2.8-2.8,7.7-2.8,10.5,0l24.7,24.7C134.8,202.2,134.5,207,131.7,209.9z" />
<path class="st3" d="M223.3,265.1c-2,2-5.4,2-7.4,0l-107.2-107c-3.7-3.7-10.2-4-13.9,0L41.8,211c-3.7,3.7-3.7,10,0,13.9L149,332.2
c2,2,2,5.4,0,7.4c-2,2-5.4,2-7.4,0L34.4,232.4c-8-8-8-20.8,0-28.7l52.9-53.2c8-8,20.8-8,28.7,0l107.2,107.2
C225.3,260,225.3,263.1,223.3,265.1z" />
<path class="st4" d="M131.7,209.9L93.6,248c-2.8,2.8-7.7,2.8-10.5,0l-24.7-24.7c-2.8-2.8-2.8-7.7,0-10.5l38.1-38.1
c2.8-2.8,7.7-2.8,10.5,0l24.7,24.7C134.8,202.2,134.5,207,131.7,209.9z" />
<path class="st3" d="M135.4,213.6l-38.1,38.1c-4.8,4.8-13.1,4.8-17.9,0L54.6,227c-4.8-4.8-4.8-13.1,0-17.9l38.1-38.1
c4.8-4.8,13.1-4.8,17.9,0l24.7,24.7C140.5,200.5,140.5,208.5,135.4,213.6z M62,216.4c-0.9,0.9-0.9,2.3,0,3.1l24.7,24.7
c0.9,0.9,2.3,0.9,3.1,0l38.1-38.1c0.9-0.9,0.9-2.3,0-3.1l-24.7-24.7c-0.9-0.9-2.3-0.9-3.1,0L62,216.4z M233.8,254.6l-95.3,95.3
c-2,2-5.4,2-7.4,0c-2-2-2-5.4,0-7.4l95.3-95.3c2-2,5.4-2,7.4,0S235.8,252.6,233.8,254.6z M228.4,238.3c-4.8,4.8-13.1,4.8-17.9,0
l-43-42.7c-0.9-0.9-2.3-0.9-3.1,0l-5.4,5.4c-2,2-5.4,2-7.4,0c-2-2-2-5.4,0-7.4l5.4-5.4c4.8-4.8,13.1-4.8,17.9,0l43,43
c0.9,0.9,2.3,0.9,3.1,0c0.9-0.9,0.9-2.3,0-3.1l-58.9-59.2c-2-2-2-5.4,0-7.4c2-2,5.4-2,7.4,0l58.9,58.9
C233.2,225.3,233.2,233.5,228.4,238.3z" />
<path class="st3" d="M174.6,163.8l-10.5,10.5c-2,2-5.4,2-7.4,0l-21.6-21.6c-2-2-2-5.4,0-7.4l1.7-1.7l-4.8-4.6c-2-2-2-5.4,0-7.4
c2-2,5.4-2,7.4,0l4.8,4.8l1.4-1.4c2-2,5.4-2,7.4,0l21.3,21.3C176.4,158.4,176.6,161.8,174.6,163.8z M160.4,163.2l3.1-3.1
l-13.9-13.9l-3.1,3.1L160.4,163.2z" />
<g>
<path class="st5" d="M163.8,290.1c-0.6,0.6-1.4,1.1-2.6,1.4c-2.8,0.6-5.7-1.1-6.3-3.7l-3.7-16.2l-3.7,5.4c-1.1,1.7-3.1,2.6-5.1,2
c-2-0.6-3.7-2-4-4l-6.5-27c-0.6-2.8,1.1-5.7,3.7-6.3c2.8-0.6,5.7,1.1,6.3,3.7l3.7,16.2l3.7-5.4c1.1-1.7,3.1-2.6,5.1-2
c2,0.6,3.7,2,4,4l6.3,27C165.5,287.3,165,289,163.8,290.1z" />
</g>
</g>
<g>
<path class="st2" d="M85,381.1l68.3,68.3c2.8,2.8,2.8,7.1,0,10l-40.1,40.1c-2.8,2.8-7.1,2.8-10,0L35,431.2c-2.8-2.8-2.8-7.1,0-10
l40.1-40.1C77.9,378.3,82.2,378.3,85,381.1z M32.7,399l20.2-20.2l11.9,11.9L44.7,411L32.7,399z" />
<path class="st5" d="M105.2,401l48.4,48.4c2.8,2.8,2.8,7.1,0,10l-40.1,40.1c-2.8,2.8-7.1,2.8-10,0l-48.4-48.4
c-2.8-2.8-2.8-7.1,0-10L95.3,401C97.8,398.2,102.4,398.2,105.2,401z" />
<path class="st6" d="M143.4,449.4L143.4,449.4c2.8-2.8,2.8-7.1,0-10L105.2,401c-2.8-2.8-7.1-2.8-10,0l-5.1,5.1l43.2,43.2
C136.2,452.2,140.5,452.2,143.4,449.4z" />
<path class="st3" d="M156.4,462.5l-40.1,40.1c-4.6,4.6-11.7,4.6-16.2,0l-68.3-68.3c-4.6-4.6-4.6-11.7,0-16.2L72,378
c4.6-4.6,11.7-4.6,16.2,0l68.3,68.3C161,450.8,161,457.9,156.4,462.5z M37.8,424.4c-1.1,1.1-1.1,2.8,0,4l68.3,68.3
c1.1,1.1,2.8,1.1,4,0l40.1-40.1c1.1-1.1,1.1-2.8,0-4l-68.3-68.3c-1.1-1.1-2.8-1.1-4,0L37.8,424.4z" />
<path class="st2" d="M111.2,487.5c-1.7,1.7-4.3,1.7-6,0l-25-25c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l25,25
C112.9,483.2,112.9,485.8,111.2,487.5z M71.1,447.4c-0.9,0.9-2,1.4-3.1,1.1c-1.1,0-2.3-0.3-3.1-1.1c-0.9-0.9-1.4-2-1.1-3.1
c0-0.3,0-0.6,0-0.9s0-0.6,0.3-0.9s0.3-0.6,0.3-0.9c0.9-1.1,2-2,3.4-2c1.1,0,2.3,0.3,3.1,1.1c0.3,0.3,0.3,0.3,0.6,0.6
c0.3,0.3,0.3,0.6,0.3,0.9c0,0.3,0.3,0.6,0.3,0.9s0,0.6,0,0.9C72.2,445.4,71.7,446.6,71.1,447.4z" />
<path class="st3" d="M68,393.9c-1.7,1.7-4.3,1.7-6,0l-9.1-9.1L39,398.8l1.1,1.1c1.7,1.7,1.7,4.3,0,6c-1.7,1.7-4.3,1.7-6,0l-4-4
c-1.7-1.7-1.7-4.3,0-6l20.2-20.2c1.7-1.7,4.3-1.7,6,0l11.9,11.9C69.7,389.7,69.7,392.2,68,393.9z" />
</g>
<g>
<path class="st2" d="M333.4,497.2l-3.1,3.1L229.5,399.6l73.4-73.4c5.4-5.4,14.5-5.4,20.2,0l70.3,70.3l0,0
C390.3,425.8,354.1,476.4,333.4,497.2z" />
<path class="st5" d="M293.5,387.7l48.1-11.7c1.7-0.3,2.6,1.7,1.1,2.6l-28.4,18.8l9.7,9.7c0.9,0.9,0.3,2.3-0.9,2.3l-43.8,6.8
c-1.4,0.3-2.3-1.7-1.1-2.6l23.6-14.5l-9.4-9.4C292.4,389.1,292.7,388,293.5,387.7z" />
<path class="st3" d="M374.6,440l-15.1-15.1c-7.1-7.1-7.1-18.8,0-26.2l8-8c1.7-1.7,4.3-1.7,6,0c1.7,1.7,1.7,4.3,0,6l-8,8
c-4,4-4,10.2,0,13.9l15.1,15.1c1.7,1.7,1.7,4.3,0,6C378.9,441.4,376,441.4,374.6,440z" />
<path class="st3" d="M327.4,503.2L226.7,402.5c-1.7-1.7-1.7-4.3,0-6l73.4-73.4c7.1-7.1,18.8-7.1,26.2,0l70.3,70.3l0,0l8,8
c1.7,1.7,1.7,4.3,0,6c-1.7,1.7-4.3,1.7-6,0l-2.3-2.3c-8.2,31.3-41.5,76.2-60,94.7l-3.1,3.1C331.7,504.9,328.8,504.9,327.4,503.2z
M235.8,399.6l94.4,94.4c21.6-21.6,54.6-69.1,58.9-96.1l-68.8-68.8c-4-4-10.2-4-13.9,0C306.1,329.4,235.8,399.6,235.8,399.6z" />
<path class="st3" d="M327.4,503.2L222.7,398.5c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l104.7,104.7c1.7,1.7,1.7,4.3,0,6
C331.7,504.9,328.8,504.9,327.4,503.2z" />
<path class="st2" d="M251.2,410.1c-5.4-5.4-13.9-5.4-19.3,0s-5.4,13.9,0,19.3l0,0c5.4,5.4,13.9,5.4,19.3,0
S256.6,415.3,251.2,410.1L251.2,410.1z M315.4,474.4c-5.4-5.4-13.9-5.4-19.3,0c-5.4,5.4-5.4,13.9,0,19.3l0,0
c5.4,5.4,13.9,5.4,19.3,0S320.9,479.8,315.4,474.4L315.4,474.4z" />
<path class="st3" d="M228.7,432.3c-7.1-7.1-6.8-18.5,0-25.3c6.8-6.8,18.5-6.8,25.3,0c6.8,6.8,6.8,18.5,0,25.3
C247.2,439.4,235.8,439.4,228.7,432.3z M248,413c-3.7-3.7-9.7-3.7-13.4,0c-3.7,3.7-3.7,9.7,0,13.4s9.7,3.7,13.4,0
C251.7,422.7,251.7,416.7,248,413z" />
<g>
<path class="st3" d="M293.3,496.6c-7.1-7.1-6.8-18.5,0-25.3c6.8-6.8,18.5-6.8,25.3,0c7.1,7.1,6.8,18.5,0,25.3
C311.5,503.7,300.1,503.7,293.3,496.6z M312.6,477.6c-3.7-3.7-9.7-3.7-13.4,0c-3.7,3.7-3.7,9.7,0,13.4s9.7,3.7,13.4,0
C316.3,487.2,316.3,481.3,312.6,477.6z" />
</g>
<g>
<path class="st3" d="M293.3,496.6l-84.5-84.5c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l84.5,84.5c1.7,1.7,1.7,4.3,0,6
C297.5,498.3,295,498.3,293.3,496.6z" />
</g>
<g>
<path class="st3" d="M194.6,398.2c-0.3-0.3-0.3-0.3-0.6-0.6c-0.3-0.3-0.3-0.6-0.3-0.9c0-0.3-0.3-0.6-0.3-0.9s0-0.6,0-0.9
c0-0.3,0-0.6,0-0.9c0-0.3,0-0.6,0.3-0.9c0.3,0,0.3-0.3,0.6-0.6s0.3-0.6,0.6-0.6c0.3,0,0.3-0.3,0.6-0.6c0.3-0.3,0.6-0.3,0.9-0.3
c0.3,0,0.6,0,0.9-0.3c0.3-0.3,0.6,0,0.9,0c0.3,0,0.6,0,0.9,0c0.3,0,0.6,0,0.9,0.3c0.3,0,0.6,0.3,0.9,0.3c0.6,0.3,0.9,0.6,1.1,1.1
c0.3,0.3,0.3,0.6,0.3,0.9c0,0.3,0.3,0.6,0.3,0.9c0,0.3,0,0.6,0,0.9s0,0.6,0,0.9c0,0.3,0,0.6-0.3,0.9c-0.3,0.3-0.3,0.6-0.3,0.9
c-0.3,0.3-0.3,0.6-0.6,0.6s-0.3,0.3-0.6,0.6c-0.3,0.3-0.6,0.3-0.9,0.3c-0.3,0-0.6,0.3-0.9,0.3s-0.6,0-0.9,0c-0.3,0-0.6,0-0.9,0
c-0.3,0-0.6,0-0.9-0.3c-0.3,0-0.6-0.3-0.9-0.3C195.1,398.8,194.8,398.5,194.6,398.2z" />
</g>
</g>
<g>
<path class="st2" d="M956.9,61.7l-38.7,38.7c-5.4,5.4-14.5,5.4-20.2,0l-39.3-39.3c-5.4-5.4-5.4-14.5,0-20.2l38.7-38.4
c5.4-5.4,14.5-5.4,20.2,0l39.3,39.3C962.3,47.2,962.3,56.3,956.9,61.7z" />
<path class="st3" d="M959.7,64.8L921,103.5c-7.1,7.1-18.8,7.1-26.2,0l-39.3-39.3c-7.1-7.1-7.1-18.8,0-26.2l38.7-38.7
c7.1-7.1,18.8-7.1,26.2,0l39.3,39.3C967.1,45.8,967.1,57.4,959.7,64.8z M861.9,44.1c-4,4-4,10.2,0,13.9l39.3,39.3
c4,4,10.2,4,13.9,0l38.7-38.7c4-4,4-10.2,0-13.9L914.5,5.4c-4-4-10.2-4-13.9,0L861.9,44.1z" />
<path class="st2" d="M925.6,69.1L925.6,69.1c-9.7,9.7-25.6,9.7-35.3,0l-0.3-0.3c-9.7-9.7-9.7-25.6,0-35.3l0,0
c9.7-9.7,25.6-9.7,35.3,0l0.3,0.3C935.3,43.8,935.3,59.4,925.6,69.1z" />
<path class="st3" d="M928.7,72.2c-11.4,11.4-29.9,11.4-41.2,0l-0.3-0.3c-11.4-11.4-11.4-29.9,0-41.2c11.4-11.4,29.9-11.4,41.2,0
l0.3,0.3C940.1,42.4,940.1,60.8,928.7,72.2z M893.2,36.7c-8,8-8,21,0,29.3l0.3,0.3c8,8,21,8,29.3,0c8-8,8-21,0-29.3l-0.3-0.3
C914.2,28.7,901.1,28.7,893.2,36.7z" />
<path class="st3" d="M896.9,55.2c-2,2-2,5.1,0,7.1c2,2,5.1,2,7.1,0l0,0c2-2,2-5.1,0-7.1C902.3,53.2,898.8,53.2,896.9,55.2
L896.9,55.2z M911.4,40.6c-2,2-2,5.1,0,7.1c2,2,5.1,2,7.1,0l0,0c2-2,2-5.1,0-7.1C916.8,38.7,913.4,38.7,911.4,40.6L911.4,40.6z" />
<path class="st2"
d="M971.9,88.7l10.8,10.8c7.4,7.4,7.4,19.3,0,26.7l0,0c-7.4,7.4-19.3,7.4-26.7,0l-10.8-10.8L971.9,88.7z" />
<path class="st3" d="M985.9,129.4c-9.1,9.1-23.6,9.1-32.7,0l-8.8-8.8c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l8.8,8.8
c5.7,5.7,15.1,5.7,20.8,0c5.7-5.7,5.7-15.1,0-20.8l-8.8-8.8c-1.7-1.7-1.7-4.3,0-6s4.3-1.7,6,0l8.8,8.8
C995,105.8,995,120.3,985.9,129.4z" />
<path class="st3" d="M953.2,112.9c-1.7,1.7-4.3,1.7-6,0l-9.4-9.4c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l9.4,9.4
C954.9,108.6,954.9,111.2,953.2,112.9z M969.4,97c-1.7,1.7-4.3,1.7-6,0l-9.4-9.4c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0
l9.4,9.4C971.1,92.4,971.1,95.3,969.4,97z" />
<path class="st3" d="M978.8,88.2l-34.1,34.1c-1.7,1.7-4.3,1.7-6,0c-1.7-1.7-1.7-4.3,0-6l34.1-34.1c1.7-1.7,4.3-1.7,6,0
C980.5,83.9,980.5,86.4,978.8,88.2z M994.4,137.9c-1.7,1.7-4.3,1.7-6,0l-8.5-8.5c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0
l8.5,8.5C995.8,133.4,996.1,136.2,994.4,137.9z" />
<g>
<path class="st5" d="M966.5,110.1c-1.4,1.4-1.4,4,0,5.4c1.4,1.4,4,1.4,5.4,0l0,0c1.4-1.4,1.4-4,0-5.4
C970.5,108.6,968,108.6,966.5,110.1L966.5,110.1z" />
</g>
</g>
<g>
<path class="st7" d="M486.4,389.4l97.6,97.6l10.8-10.8c6-6,6-15.6,0-21.6l-38.7-38.7l-1.7-0.6c-6.5-2.3-8.5-10.5-3.7-15.6
l-3.4-3.4C529.9,378.6,509.4,379.4,486.4,389.4" />
<path class="st8" d="M517.7,382.6C517.4,382.6,517.4,382.6,517.7,382.6c-1.1-0.3-2-0.3-2.8-0.3c-8.8,0.3-18.2,2.8-28.2,6.8l0,0
l31,31C527.9,410.1,527.9,393.1,517.7,382.6 M599.3,465c-10.2-7.7-25-6.8-34.7,2.6l19.3,19.3l10.8-10.8
C597.9,473,599.3,469,599.3,465" />
<path class="st3" d="M581.1,490.1l-97.6-97.8c-1.1-1.1-1.4-2.3-1.1-3.7c0.3-1.4,1.1-2.6,2.6-3.1c27.9-11.9,48.6-9.4,65.7,8
l0.3,0.3c1.7,1.7,1.7,4.3,0,6s-4.3,1.7-6,0l-0.3-0.3c-13.4-13.4-29.3-16.2-50.6-8.2L584,481l8-8c4.3-4.3,4.3-11.4,0-15.6
l-34.4-34.4c-1.1-1.1-1.4-2.3-1.1-3.7l1.7-10.8c0.3-2.3,2.6-4,4.8-3.4c2.3,0.3,4,2.6,3.4,4.8l-1.4,8.5l33,33
c7.7,7.7,7.7,20.2,0,27.9l-10.8,10.8C585.4,491.8,582.8,491.5,581.1,490.1z" />
<path class="st2" d="M509.4,390.5c-6-6-15.6-6-21.6,0c-6,6-6,15.6,0,21.6l0,0c6,6,15.6,6,21.6,0
C515.7,406.4,515.7,396.8,509.4,390.5L509.4,390.5z M565.8,434.6c-3.4-3.4-8.8-3.4-11.9,0c-3.1,3.4-3.4,8.8,0,11.9
c3.4,3.1,8.8,3.4,11.9,0C569.2,443.4,569.2,438,565.8,434.6z" />
<path class="st3" d="M484.7,415.3c-7.7-7.7-7.7-20.2,0-27.9s20.2-7.7,27.9,0c7.7,7.7,7.7,20.2,0,27.9
C504.9,422.9,492.4,422.9,484.7,415.3z M506.6,393.6c-4.3-4.3-11.4-4.3-15.6,0c-4.3,4.3-4.3,11.4,0,15.6c4.3,4.3,11.4,4.3,15.6,0
C510.9,405,510.9,397.9,506.6,393.6z" />
<path class="st2" d="M594.5,475.6c-6-6-15.6-6-21.6,0s-6,15.6,0,21.6l0,0c6,6,15.6,6,21.6,0S600.5,481.5,594.5,475.6L594.5,475.6z
" />
<path class="st3" d="M569.7,500.3c-7.7-7.7-7.7-20.2,0-27.9s20.2-7.7,27.9,0s7.7,20.2,0,27.9C589.9,508,577.4,508,569.7,500.3z
M591.4,478.4c-4.3-4.3-11.4-4.3-15.6,0s-4.3,11.4,0,15.6s11.4,4.3,15.6,0S595.6,483,591.4,478.4z" />
<path class="st3" d="M569.7,500.3l-98.1-98.1c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l98.1,98.1c1.7,1.7,1.7,4.3,0,6
C574,502,571.4,502,569.7,500.3z M459.7,390.2c-0.9-0.9-1.4-2-1.1-3.1c0-1.1,0.3-2.3,1.1-3.1c0.9-0.9,2-1.4,3.1-1.1
c2.3,0,4.3,2,4.3,4.3c0,1.1-0.3,2.3-1.1,3.1c-0.9,0.9-2,1.4-3.1,1.1C461.4,391.4,460.5,391.1,459.7,390.2z" />
</g>
<g>
<path class="st2" d="M306.9,39.8l-35-35.3c-7.7-7.7-19.9-8.2-28.2-1.7L218.2,23l70.3,70.3L308.6,68
C315.4,59.4,314.6,47.2,306.9,39.8z" />
<path class="st2"
d="M306.9,39.8l-17.6-17.6l-36.1,36.1l35.3,35.3l20.2-25.3C315.4,59.4,314.6,47.2,306.9,39.8z" />
<path class="st9" d="M223.9,17l70.3,70.3L260,121.4l-70.3-70.3L223.9,17z" />
<path class="st10" d="M259.1,52l35.3,35.3l-34.1,34.1l-35.3-35L259.1,52z" />
<path class="st9" d="M217.9,23L217.9,23c-5.4-5.4-14.5-5.4-20.2,0l-18.2,18.2l20.2,20.2l18.2-18.2C223.6,37.5,223.6,28.4,217.9,23
z M270.2,131.7l18.2-18.2c5.4-5.4,5.4-14.5,0-20.2l0,0c-5.4-5.4-14.5-5.4-20.2,0L250,111.5" />
<path class="st3" d="M196.8,64.3c-1.7-1.7-1.7-4.3,0-6L215,40.1c4-4,4-10.2,0-13.9c-4-4-10.2-4-13.9,0l-18.2,18.2
c-1.7,1.7-4.3,1.7-6,0s-1.7-4.3,0-6l18.2-18.2c7.1-7.1,18.8-7.1,26.2,0s7.1,18.8,0,26.2l-18.2,18.2C201.1,66,198.5,66,196.8,64.3z
M267.1,134.5c-1.7-1.7-1.7-4.3,0-6l18.2-18.2c4-4,4-10.2,0-13.9c-4-3.7-10.2-4-13.9,0l-18.2,18.2c-1.7,1.7-4.3,1.7-6,0
c-1.7-1.7-1.7-4.3,0-6l18.2-18.2c7.1-7.1,18.8-7.1,26.2,0c7.1,7.1,7.1,18.8,0,26.2l-18.2,18.2
C271.6,136.2,268.8,136.2,267.1,134.5z" />
<path class="st3" d="M289.6,90.1l-0.3-0.3c-19.1-25.9-41.8-48.6-67.4-67.7c-1.7-1.4-2.3-4-0.9-6c1.4-1.7,4-2.3,6-0.9
c26.5,19.6,49.8,43,69.4,69.4c1.4,1.7,1.1,4.6-0.9,6C293.5,91.6,291.3,91.6,289.6,90.1z" />
<path class="st3" d="M215,25.9l-0.3-0.3c-1.4-1.7-1.1-4.6,0.6-6l25.6-20.2c10.2-8,24.7-7.1,34.1,2l35,35.3c9.4,9.4,10,23.9,2,34.1
l-20.2,25.3c-1.4,1.7-4,2-6,0.6c-2-1.4-2-4-0.6-6l20.2-25.3c5.4-6.8,4.8-16.5-1.1-22.8L268.8,7.4c-6.3-6.3-15.9-6.8-22.8-1.4
l-25.6,20.2C218.7,27.8,216.5,27.6,215,25.9z M264.2,125.7l-13.9,13.9l-8-8l13.9-13.9 M193.7,55.2l-13.9,13.9l-8-8l13.9-13.9" />
<path class="st3" d="M247.2,142.8l-8.2-8.2c-1.7-1.7-1.7-4.3,0-6l13.9-13.9c1.7-1.7,4.3-1.7,6,0s1.7,4.3,0,6l-11.1,11.1l2,2
l11.1-11.1c1.7-1.7,4.3-1.7,6,0c1.7,1.7,1.7,4.3,0,6l-13.7,14.2C251.4,144.5,248.9,144.5,247.2,142.8z M176.6,72.2l-8-8
c-1.7-1.7-1.7-4.3,0-6l13.9-13.9c1.7-1.7,4.3-1.7,6,0s1.7,4.3,0,6l-11.1,11.1l2,2l11.1-11.1c1.7-1.7,4.3-1.7,6,0
c1.7,1.7,1.7,4.3,0,6l-13.9,13.9C180.9,73.9,178.3,73.9,176.6,72.2z" />
<path class="st3" d="M270.2,137.6l-96.4-96.4c-1.7-1.7-1.7-4.3,0-6s4.3-1.7,6,0l96.4,96.4c1.7,1.7,1.7,4.3,0,6
C274.5,139.4,271.9,139.4,270.2,137.6z" />
<path class="st2" d="M239.5,88.7l-16.8-16.8c-1.7-1.7-1.7-4.3,0-6l10-10c6.3-6.3,16.5-6.3,23,0l0,0c6.3,6.3,6.3,16.5,0,23l-10,10
C243.8,90.4,241.2,90.4,239.5,88.7z" />
<path class="st3" d="M302.4,29l-3.1,3.1L279,11.9l3.1-3.1l0,0c3.4-3.4,8.8-3.4,11.9,0l8,8C305.8,20.2,305.8,25.6,302.4,29
L302.4,29z" />
<path class="st2" d="M213,28.1c-2.8-2.8-7.4-2.8-10,0c-2.8,2.8-2.8,7.4,0,10c2.8,2.8,7.4,2.8,10,0C215.6,35.2,215.6,30.7,213,28.1
z M283.3,98.4c-2.8-2.8-7.4-2.8-10,0s-2.8,7.4,0,10s7.4,2.8,10,0C285.9,105.5,286.2,101.2,283.3,98.4z" />
<path class="st3" d="M253.2,148.7L169,64.5c-1.7-1.7-1.7-4.3,0-6s4.3-1.7,6,0l84.2,84.2c1.7,1.7,1.7,4.3,0,6
C257.4,150.4,254.9,150.4,253.2,148.7z M274.2,157.8c-1.7-1.7-4.3-1.7-6,0s-1.7,4.3,0,6c1.7,1.7,4.3,1.7,6,0
C275.9,162.1,275.9,159.5,274.2,157.8z" />
</g>
<g>
<path class="st2" d="M395.7,154.4l68.3,68.3c2.8,2.8,2.8,7.1,0,10l-40.1,40.1c-2.8,2.8-7.1,2.8-10,0l-68.3-68.3
c-2.8-2.8-2.8-7.1,0-10l40.1-40.1C388.3,151.6,392.8,151.6,395.7,154.4z M343.3,172.3l20.2-20.2l11.9,11.9l-20.2,20.2L343.3,172.3
z" />
<path class="st5" d="M415.6,174.3l48.4,48.4c2.8,2.8,2.8,7.1,0,10l-40.1,40.1c-2.8,2.8-7.1,2.8-10,0l-48.4-48.4
c-2.8-2.8-2.8-7.1,0-10l40.1-40.1C408.5,171.5,412.7,171.5,415.6,174.3z" />
<path class="st6" d="M454,222.7L454,222.7c2.8-2.8,2.8-7.1,0-10l-38.1-38.1c-2.8-2.8-7.1-2.8-10,0l-5.1,5.1l43,43
C446.6,225.5,451.1,225.5,454,222.7z" />
<path class="st3" d="M467.1,235.8l-40.4,40.1c-4.6,4.6-11.7,4.6-16.2,0l-68.3-68.3c-4.6-4.6-4.6-11.7,0-16.2l40.1-40.1
c4.6-4.6,11.7-4.6,16.2,0l68.3,68.3C471.3,224.1,471.3,231.2,467.1,235.8z M348.2,197.7c-1.1,1.1-1.1,2.8,0,4l68.3,68.3
c1.1,1.1,2.8,1.1,4,0l40.4-40.1c1.1-1.1,1.1-2.8,0-4l-68.3-68.3c-1.1-1.1-2.8-1.1-4,0L348.2,197.7z" />
<path class="st2" d="M421.5,260.8c-1.7,1.7-4.3,1.7-6,0l-25-25c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l25,25
C423.3,256.5,423.3,259.1,421.5,260.8z M381.4,220.7c-0.9,0.9-2,1.4-3.1,1.1c-1.1,0-2.3-0.3-3.1-1.1c-0.9-0.9-1.4-2-1.1-3.1
c0-0.3,0-0.6,0-0.9c0-0.3,0-0.6,0.3-0.9c0.3-0.3,0.3-0.6,0.3-0.9c0.9-1.1,2-2,3.4-2c1.1,0,2.3,0.3,3.1,1.1
c0.3,0.3,0.3,0.3,0.6,0.6c0.3,0.3,0.3,0.6,0.3,0.9c0,0.3,0.3,0.6,0.3,0.9c0,0.3,0,0.6,0,0.9C382.9,218.7,382.3,219.8,381.4,220.7z
" />
<path class="st3" d="M378.3,167.5c-1.7,1.7-4.3,1.7-6,0l-9.1-9.1l-13.9,13.9l1.1,1.1c1.7,1.7,1.7,4.3,0,6s-4.3,1.7-6,0l-4-4
c-1.7-1.7-1.7-4.3,0-6l20.2-20.2c1.7-1.7,4.3-1.7,6,0l11.9,11.9C380,163,380,165.8,378.3,167.5z" />
</g>
<g>
<path class="st1" d="M896,289.5l8.5,8.5l-5.4,5.4l21.6,21.6l10.5-10.5L909.7,293l-5.1,5.1" />
<path class="st2"
d="M905.4,490.4L798.2,383.1c-6-6-6-15.4,0-21.3l53.2-53.2c5.7-5.7,15.4-5.7,21.3,0l107.2,107.2" />
<path class="st2" d="M892,364.3l-38.1,38.1c-2.8,2.8-7.7,2.8-10.5,0l-24.7-24.7c-2.8-2.8-2.8-7.7,0-10.5l38.1-38.1
c2.8-2.8,7.7-2.8,10.5,0l24.7,24.7C894.9,356.7,894.9,361.5,892,364.3z" />
<path class="st3" d="M983.3,419.5c-2,2-5.4,2-7.4,0l-107-107c-3.7-3.7-10.2-4-13.9,0l-53.2,53.2c-3.7,3.7-3.7,10,0,13.9
l107.2,107.2c2,2,2,5.4,0,7.4c-2,2-5.4,2-7.4,0L794.5,387.1c-8-8-8-20.8,0-28.7l53.2-53.2c8-8,20.8-8,28.7,0l107.2,107.2
C985.6,414.4,985.3,417.5,983.3,419.5z" />
<path class="st4" d="M892,364.3l-38.1,38.1c-2.8,2.8-7.7,2.8-10.5,0l-24.7-24.7c-2.8-2.8-2.8-7.7,0-10.5l38.1-38.1
c2.8-2.8,7.7-2.8,10.5,0l24.7,24.7C894.9,356.7,894.9,361.5,892,364.3z" />
<path class="st3" d="M895.7,368l-38.1,38.1c-4.8,4.8-13.1,4.8-17.9,0l-24.7-24.7c-4.8-4.8-4.8-13.1,0-17.9l38.1-38.1
c4.8-4.8,13.1-4.8,17.9,0l24.7,24.7C900.6,355,900.6,362.9,895.7,368z M822.3,370.9c-0.9,0.9-0.9,2.3,0,3.1l24.7,24.7
c0.9,0.9,2.3,0.9,3.1,0l38.1-38.1c0.9-0.9,0.9-2.3,0-3.1l-24.7-24.7c-0.9-0.9-2.3-0.9-3.1,0L822.3,370.9z M994.1,409l-95.3,95.3
c-2,2-5.4,2-7.4,0c-2-2-2-5.4,0-7.4l95.3-95.3c2-2,5.4-2,7.4,0C996.1,403.6,996.1,407,994.1,409z M988.4,392.8
c-4.8,4.8-13.1,4.8-17.9,0l-43-43c-0.9-0.9-2.3-0.9-3.1,0l-5.4,5.4c-2,2-5.4,2-7.4,0c-2-2-2-5.4,0-7.4l5.4-5.4
c4.8-4.8,13.1-4.8,17.9,0l43,43c0.9,0.9,2.3,0.9,3.1,0c0.9-0.9,0.9-2.3,0-3.1l-58.9-58.9c-2-2-2-5.4,0-7.4c2-2,5.4-2,7.4,0
l58.9,58.9C993.3,379.7,993.3,388,988.4,392.8z" />
<path class="st3" d="M934.7,318.3l-10.5,10.5c-2,2-5.4,2-7.4,0l-21.6-21.6c-2-2-2-5.4,0-7.4l1.7-1.7l-4.8-4.8c-2-2-2-5.4,0-7.4
c2-2,5.4-2,7.4,0l4.8,4.8l1.4-1.4c2-2,5.4-2,7.4,0l21.3,21.3C936.7,312.9,936.7,316.3,934.7,318.3z M920.5,317.7l3.1-3.1
l-13.9-13.9l-3.1,3.1L920.5,317.7z" />
<g>
<path class="st5" d="M923.9,444.6c-0.6,0.6-1.4,1.1-2.6,1.4c-2.8,0.6-5.7-1.1-6.3-3.7l-3.7-16.2l-3.7,5.4c-1.1,1.7-3.1,2.6-5.1,2
c-2-0.6-3.7-2-4-4l-6.5-27c-0.6-2.8,1.1-5.7,3.7-6.3c2.8-0.6,5.7,1.1,6.3,3.7l3.7,16.2l3.7-5.4c1.1-1.7,3.1-2.6,5.1-2
c2,0.6,3.7,2,4,4l6.3,27C925.6,441.7,925.3,443.4,923.9,444.6z" />
</g>
</g>
<g>
<path class="st11"
d="M665,452.5l43.5,43.5l45.2-74.5c0.9-1.4,0.9-3.1-0.6-4.6l-9.4-9.4c-1.1-1.1-2.8-1.4-4.6-0.6L665,452.5z" />
<path class="st2" d="M695.5,434l-17.6,10.8l38.4,38.4l0,0l10.8-17.6L695.5,434z M728.5,413.8l-17.6,10.8l25.6,25.6l10.8-17.6
L728.5,413.8z" />
<path class="st12" d="M749.8,413.3l3.7,3.7c1.1,1.1,1.4,2.8,0.6,4.6L708.6,496l-10-10l51.8-68.3
C751.2,416.1,750.9,414.4,749.8,413.3z" />
<g>
<path class="st3" d="M706.6,498L663,454.5c-0.6-0.6-0.9-1.4-0.9-2.3c0-0.9,0.6-1.7,1.4-2l74.5-45.2c2.6-1.4,5.7-1.1,8,0.9
l9.4,9.4c2,2,2.6,5.4,0.9,8l-45.5,74.2c-0.6,0.9-1.1,1.1-2,1.4C708,498.9,707.1,498.6,706.6,498z M669.6,453.1l38.4,38.4
l43.5-71.7c0.3-0.3,0.3-0.9,0-0.9l-9.4-9.4c-0.3-0.3-0.6-0.3-0.9,0L669.6,453.1z" />
<path class="st3" d="M655.6,447.1c-1.1-1.1-1.1-2.8,0-4s2.8-1.1,4,0l58,58l0,0c1.1,1.1,1.1,2.8,0,4s-2.8,1.1-4,0L655.6,447.1
L655.6,447.1z" />
</g>
</g>
<g>
<path class="st2" d="M523.4,274.7l35.8-36.1c11.1-11.1,29.3-11.1,40.1,0l64.3,64.3c7.7,7.7,10.2,19.1,7.1,29.3l-1.1,3.4
c-6.3,18.2-17.6,34.4-32.7,46.6l-5.7,4.8c-3.1,2.6-7.1-2-4.3-4.8l0,0" />
<path class="st2" d="M642.8,376.3l0.3,0.3l0,0c3.4-3.4,6.8-7.4,10-11.1l0.3-0.3c0.6-0.6,1.1-1.4,1.4-2c0.3-0.3,0.3-0.3,0.3-0.6
c0.6-0.6,0.9-1.4,1.4-2c0.3-0.3,0.3-0.6,0.6-0.6c0.3-0.6,0.9-1.4,1.4-2c0.3-0.3,0.3-0.3,0.3-0.6c0.9-1.4,1.7-2.8,2.6-4
c0,0,0.3-0.3,0.3-0.6c0.3-0.6,0.6-1.1,0.9-1.7c0.3-0.6,0.6-0.9,0.9-1.4s0.6-0.9,0.6-1.4c0.3-0.6,0.6-1.1,0.9-1.7
c0.3-0.3,0.6-0.9,0.6-1.4c0.3-0.6,0.6-1.1,0.9-1.7c0.3-0.3,0.3-0.9,0.6-1.4c0.3-0.6,0.6-1.4,0.9-2c0-0.3,0.3-0.6,0.3-1.1
c0.3-1.1,0.6-2,1.1-3.1l1.1-3.4c3.4-10.2,0.6-21.6-7.1-29.3l-64-64c-11.1-11.1-29.3-11.1-40.1,0l-8,8l87.6,87.6
C650,345.6,651.7,362.9,642.8,376.3z" />
<path class="st2" d="M595.1,278.7l-10-10c-2.8-2.8-2.8-7.1,0-10l2-2c2.8-2.8,7.1-2.8,10,0l10,10c2.8,2.8,2.8,7.1,0,10l-2,2
C602.2,281.6,597.9,281.3,595.1,278.7z M620.1,303.8l-10-10c-2.8-2.8-2.8-7.1,0-10l2-2c2.8-2.8,7.1-2.8,10,0l10,10
c2.8,2.8,2.8,7.1,0,10l-2,2C627.5,306.6,622.9,306.6,620.1,303.8z M664.2,347.8c0.3-0.6,0.6-1.1,0.9-1.7c0.3-0.3,0.6-0.9,0.6-1.4
c0.3-0.6,0.6-1.1,0.9-1.7c0.3-0.3,0.3-0.9,0.6-1.4c0.3-0.6,0.6-1.4,0.9-2c0-0.3,0.3-0.6,0.3-1.1c0.3-1.1,0.6-2,1.1-3.1l1.1-3.4
c0.3-0.6,0.3-1.1,0.6-2l-23.3-23.3c-2.8-2.8-7.1-2.8-10,0l-2.3,2.3c-2.8,2.8-2.8,7.1,0,10L664.2,347.8L664.2,347.8z" />
<path class="st3" d="M623.8,389.9l-0.3-0.3c-2.8-2.8-2.6-7.4,0.3-10.2c1.7-1.7,4.3-1.7,6,0c0.6,0.6,1.1,1.4,1.1,2.3l3.1-2.6
c14.5-11.7,25-27.3,31.3-44.9l1.1-3.4c2.8-8.8,0.6-18.2-6-24.7l-64.3-64.3c-9.4-9.4-24.7-9.4-34.1,0l-35.8,35.8
c-1.7,1.7-4.3,1.7-6,0c-1.7-1.7-1.7-4.3,0-6l35.8-35.8c12.8-12.8,33.6-12.8,46.4,0l64.3,64.3c8.8,8.8,11.9,21.6,8,33.6l-1.1,3.4
c-6.5,19.1-18.2,35.8-33.8,48.6l-5.7,4.8C630.6,392.8,626.6,392.5,623.8,389.9z M644.8,388.2c-1.7-1.7-1.7-4.3,0-6l18.2-18.2
c1.7-1.7,4.3-1.7,6,0c1.7,1.7,1.7,4.3,0,6l-18.2,18.2C649.4,389.9,646.5,389.9,644.8,388.2z" />
<path class="st3" d="M599.6,361.2l-46.9-46.9c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l47.2,46.9c1.7,1.7,1.7,4.3,0,6
C604.2,362.9,601.3,362.9,599.6,361.2z M531.3,292.7L516,277.3c-1.7-1.7-1.7-4.3,0-6s4.3-1.7,6,0l15.4,15.4c1.7,1.7,1.7,4.3,0,6
C535.6,294.4,533,294.4,531.3,292.7z M671.9,299.2l-66.6-66.6c-1.7-1.7-1.7-4.3,0-6s4.3-1.7,6,0l66.6,66.6c1.7,1.7,1.7,4.3,0,6
S673.6,300.9,671.9,299.2z M640.3,379.7c-1.7-1.7-1.7-4-0.3-6c7.7-8.8,5.4-26.5-4.6-36.4l-87.6-87.6c-1.7-1.7-1.7-4.3,0-6
s4.3-1.7,6,0l87.6,87.6c13.1,13.1,15.4,35.8,4.8,48.1C644.8,381.1,642.3,381.4,640.3,379.7L640.3,379.7z M565.2,246.9l-6.3-6.3
c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l6.3,6.3c1.7,1.7,1.7,4.3,0,6C569.7,248.6,566.9,248.6,565.2,246.9z M577.4,242.9
l-6.3-6.3c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l6.3,6.3c1.7,1.7,1.7,4.3,0,6C581.7,244.6,579.1,244.6,577.4,242.9z" />
<path class="st2" d="M623.5,359.5c-5.4-5.4-13.9-5.4-19.3,0c-5.4,5.4-5.4,13.9,0,19.3l0,0c5.4,5.4,13.9,5.4,19.3,0
C628.6,373.4,628.6,364.9,623.5,359.5L623.5,359.5z" />
<path class="st3" d="M601,382c-7.1-7.1-6.8-18.5,0-25.3c6.8-6.8,18.5-6.8,25.3,0c6.8,6.8,7.1,18.5,0,25.3
C619.5,389.1,608.1,388.8,601,382z M620.4,362.6c-3.7-3.7-9.7-3.7-13.4,0c-3.7,3.7-3.7,9.7,0,13.4c3.7,3.7,9.7,3.7,13.4,0
C624.1,372.3,624.1,366.3,620.4,362.6z" />
<path class="st2" d="M535.3,290.7c-5.4,5.4-5.4,13.9,0,19.3c5.4,5.4,13.9,5.4,19.3,0c2.3-2.3,3.7-5.1,4-8.2l-15.1-15.1
C540.4,287.3,537.6,288.4,535.3,290.7z" />
<path class="st3" d="M532.5,313.1c-7.1-7.1-6.8-18.5,0-25.3c2.8-2.8,6.5-4.6,10.8-5.1c1.4,0,2.6,0.3,3.4,1.1l15.1,15.1
c0.9,0.9,1.4,2.3,1.1,3.4c-0.3,4-2.3,8-5.1,10.8C550.7,320.3,539.3,320,532.5,313.1z M542.2,291.5c-1.4,0.3-2.6,1.1-3.7,2.3
c-3.7,3.7-3.7,9.7,0,13.4c3.7,3.7,9.7,3.7,13.4,0c1.1-1.1,2-2.3,2.3-3.7L542.2,291.5z" />
<g>
<path class="st3" d="M619.2,244.6c-0.6-0.6-1.1-1.7-1.1-2.8l-0.6-14.8c0-2.3,1.7-4.3,4-4.6c2.3,0,4.3,1.7,4.6,4l0.6,14.8
c0,2.3-1.7,4.3-4,4.6C621.2,245.7,620.1,245.2,619.2,244.6z" />
</g>
<g>
<path class="st3" d="M623.8,385.4l-2.6-2.6c-1.7-1.7-1.7-4.3,0-6s4.3-1.7,6,0l2.6,2.6c1.7,1.7,1.7,4.3,0,6
C628.1,387.1,625.5,387.1,623.8,385.4z" />
</g>
</g>
<g>
<path class="st2" d="M554.4,172.9l-3.1,3.1L450.6,75.4L523.9,2c5.4-5.4,14.5-5.4,20.2,0l70.3,70.3l0,0
C611.3,101.5,575.1,152.2,554.4,172.9z" />
<path class="st5" d="M514.8,63.4l48.1-11.7c1.7-0.3,2.6,1.7,1.1,2.6l-28.4,18.8l9.7,9.7c0.9,0.9,0.3,2.3-0.9,2.3l-43.8,6.8
c-1.4,0.3-2.3-1.7-1.1-2.6l23.6-14.5l-9.4-9.4C513.4,64.8,513.7,63.7,514.8,63.4z" />
<path class="st3" d="M595.6,115.5l-15.1-15.1c-7.1-7.1-7.1-18.8,0-26.2l8-8c1.7-1.7,4.3-1.7,6,0c1.7,1.7,1.7,4.3,0,6l-8,8
c-4,4-4,10.2,0,13.9l15.1,15.1c1.7,1.7,1.7,4.3,0,6C599.9,117.2,597.3,117.2,595.6,115.5z" />
<path class="st3" d="M548.4,178.9L447.7,78.2c-1.7-1.7-1.7-4.3,0-6l73.4-73.4c7.1-7.1,18.8-7.1,26.2,0l70.3,70.3l0,0l8,8
c1.7,1.7,1.7,4.3,0,6c-1.7,1.7-4.3,1.7-6,0l-2.3-2.3c-8.2,31.3-41.5,76.2-60,94.7l-3.1,3.1C552.7,180.6,549.8,180.6,548.4,178.9z
M456.8,75.4l94.4,94.4c21.6-21.6,54.6-69.1,58.9-96.1L541.3,4.8c-4-4-10.2-4-13.9,0C527.1,5.1,456.8,75.4,456.8,75.4z" />
<path class="st3" d="M548.4,178.9L443.7,74.2c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l104.7,104.7c1.7,1.7,1.7,4.3,0,6
C552.7,180.6,549.8,180.6,548.4,178.9z" />
<path class="st2" d="M472.2,85.6c-5.4-5.4-13.9-5.4-19.3,0c-5.4,5.4-5.4,13.9,0,19.3l0,0c5.4,5.4,13.9,5.4,19.3,0
C477.6,99.8,477.6,91,472.2,85.6L472.2,85.6z M536.5,150.2c-5.4-5.4-13.9-5.4-19.3,0c-5.4,5.4-5.4,13.9,0,19.3l0,0
c5.4,5.4,13.9,5.4,19.3,0C541.9,164.1,541.9,155.3,536.5,150.2L536.5,150.2z" />
<path class="st3" d="M450,108.1c-7.1-7.1-6.8-18.5,0-25.3c6.8-6.8,18.5-6.8,25.3,0c6.8,6.8,6.8,18.5,0,25.3
C468.2,114.9,456.8,115.2,450,108.1z M469,88.7c-3.7-3.7-9.7-3.7-13.4,0s-3.7,9.7,0,13.4c3.7,3.7,9.7,3.7,13.4,0
C472.7,98.4,473,92.4,469,88.7z" />
<g>
<path class="st3" d="M514.3,172.3c-7.1-7.1-6.8-18.5,0-25.3c6.8-6.8,18.5-6.8,25.3,0c7.1,7.1,6.8,18.5,0,25.3
C532.8,179.5,521.4,179.5,514.3,172.3z M533.6,153c-3.7-3.7-9.7-3.7-13.4,0c-3.7,3.7-3.7,9.7,0,13.4c3.7,3.7,9.7,3.7,13.4,0
C537,162.7,537.3,156.7,533.6,153z" />
</g>
<g>
<path class="st3" d="M514.3,172.3l-84.5-84.5c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l84.5,84.5c1.7,1.7,1.7,4.3,0,6
C518.5,174.1,516,174.1,514.3,172.3z" />
</g>
<g>
<path class="st3" d="M415.6,73.9c-0.3-0.3-0.3-0.3-0.6-0.6c0-0.3-0.3-0.6-0.3-0.9s-0.3-0.6-0.3-0.9s0-0.6,0-0.9s0-0.6,0-0.9
c0-0.3,0-0.6,0.3-0.9c0.3,0,0.3-0.3,0.6-0.6c0.3-0.3,0.3-0.6,0.6-0.6c0.3,0,0.3-0.3,0.6-0.6c0.3-0.3,0.6-0.3,0.9-0.3
s0.6,0,0.9-0.3s0.6,0,0.9,0c0.3,0,0.6,0,0.9,0c0.3,0,0.6,0,0.9,0.3c0.3,0.3,0.6,0.3,0.9,0.3c0.6,0.3,0.9,0.6,1.1,1.1
c0.3,0.3,0.3,0.6,0.3,0.9s0.3,0.6,0.3,0.9c0,0.3,0,0.6,0,0.9c0,0.3,0,0.6,0,0.9s0,0.6-0.3,0.9c0,0.3-0.3,0.6-0.3,0.9
c-0.3,0.3-0.3,0.6-0.6,0.6c-0.3,0-0.3,0.3-0.6,0.6c-0.3,0.3-0.6,0.3-0.9,0.3s-0.6,0.3-0.9,0.3c-0.3,0-0.6,0-0.9,0
c-0.3,0-0.6,0-0.9,0s-0.6,0-0.9-0.3c-0.3,0-0.6-0.3-0.9-0.3C416.1,74.5,415.9,73.9,415.6,73.9z" />
</g>
</g>
<path class="st3"
d="M85.9,54.3l8.8,8.8l0,0l0,0c-5.7,5.4-11.1,9.7-17.6,5.1L76.2,68l-1.7-1.7" />
<path class="st3" d="M92.7,65.1l-8.8-8.5c-1.1-1.1-1.1-2.8,0-4s2.8-1.1,4,0l8.8,8.8c1.1,1.1,1.1,2.8,0,4
C95.6,66.5,93.9,66.2,92.7,65.1z M107.2,33.3l8.8,8.8l0,0l0,0c-5.7,5.4-11.1,9.7-17.6,5.1l-0.9-0.6l-1.7-1.7" />
<path class="st3" d="M113.8,44.1l-8.5-8.8c-1.1-1.1-1.1-2.8,0-4c1.1-1.1,2.8-1.1,4,0l8.8,8.8c1.1,1.1,1.1,2.8,0,4
C116.9,45.2,115.2,45.2,113.8,44.1z M59.2,27.6l-8.8-8.8l0,0l0,0c-5.4,5.7-9.7,11.1-5.1,17.6l0.6,0.9l1.7,1.7" />
<path class="st3" d="M57.2,29.6l-8.8-8.8c-1.1-1.1-1.1-2.8,0-4c1.1-1.1,2.8-1.1,4,0l8.8,8.8c1.1,1.1,1.1,2.8,0,4S58,30.7,57.2,29.6
z M80.2,6.2l-8.8-8.8l0,0l0,0C66,3.1,61.7,8.5,66.3,15l0.6,0.9l1.7,1.7" />
<path class="st3" d="M78.2,8.2l-8.8-8.5c-1.1-1.1-1.1-2.8,0-4c1.1-1.1,2.8-1.1,4,0l8.8,8.8c1.1,1.1,1.1,2.8,0,4
C81.4,9.6,79.4,9.4,78.2,8.2z" />
<g>
<path class="st2" d="M57.7,69.9L44.7,56.6c-3.7-3.7-3.7-10,0-13.7L82.2,5.4c3.7-3.7,10-3.7,13.7,0l13.4,13.4
c3.7,3.7,3.7,10,0,13.7L71.4,69.9C67.7,73.6,61.7,73.6,57.7,69.9z" />
</g>
<g>
<path class="st11" d="M95.6,18.2c-4.3-4.3-11.1-4.3-15.1,0c-4.3,4.3-4.3,11.1,0,15.1l0,0c4.3,4.3,11.1,4.3,15.1,0
S99.6,22.2,95.6,18.2L95.6,18.2z" />
</g>
<g>
<path class="st2" d="M74,39.5c-4.3-4.3-11.1-4.3-15.1,0s-4.3,11.1,0,15.1l0,0c4.3,4.3,11.1,4.3,15.1,0
C78.2,50.6,78.2,43.8,74,39.5L74,39.5z" />
</g>
<g>
<path class="st3" d="M55.8,71.9L42.4,58.6c-4.8-4.8-4.8-12.8,0-17.6L79.9,3.4c4.8-4.8,12.8-4.8,17.6,0l13.4,13.4
c4.8,4.8,4.8,12.8,0,17.6L73.7,71.9C68.6,76.8,60.9,76.8,55.8,71.9z M93.9,7.4c-2.6-2.6-6.8-2.6-9.7,0L46.6,44.9
c-2.6,2.6-2.6,6.8,0,9.7L60,68c2.6,2.6,6.8,2.6,9.7,0l37.5-37.5c2.6-2.6,2.6-6.8,0-9.7L93.9,7.4z" />
</g>
<g>
<path class="st3" d="M29.3,84.2c-1.1-1.1-1.1-2.8,0-4l19.1-19.1c1.1-1.1,2.8-1.1,4,0s1.1,2.8,0,4L33.3,84.2
C32.4,85.3,30.4,85.3,29.3,84.2z" />
</g>
<g>
<path class="st5" d="M802.4,224.7l-70.5-70.5l-46.9,46.9c-4.3,4.3-4.3,11.4,0,15.6l54.9,54.9c4.3,4.3,11.4,4.3,15.6,0L802.4,224.7
z" />
<path class="st2" d="M731,207.9l33.3-8.2c1.1-0.3,1.7,1.1,0.9,1.7l-19.6,13.1l6.8,6.8c0.6,0.6,0.3,1.4-0.6,1.7l-30.2,4.8
c-1.1,0.3-1.4-1.1-0.6-1.7l16.2-9.7l-6.5-6.5C729.9,209,730.2,208.2,731,207.9z" />
<path class="st6" d="M794.7,217l-45.5,45.5c-0.9,0.9-2.3,0.9-3.1,0L712.5,229c-2.3-2.3-5.7-2.3-8,0l0,0c-2.3,2.3-2.3,5.7,0,8
l35.3,35.3c4.3,4.3,11.4,4.3,15.6,0l46.9-46.9L794.7,217z" />
<path class="st2" d="M737.6,141.1l78.2,78.2l-9.4,9.4l-78.2-78.2L737.6,141.1z M760.9,164.4l9.4-9.4l-11.7-11.7l-9.4,9.4
M804.1,207.6l9.4-9.4l-11.7-11.7l-9.4,9.4" />
<path class="st3" d="M737.6,274.2l-54.9-54.9c-5.7-5.7-5.7-14.8,0-20.5l39.3-39.3c1.4-1.4,3.4-1.4,4.8,0s1.4,3.4,0,4.8l-39.3,39.3
c-3.1,3.1-3.1,8,0,11.1l54.9,54.9c3.1,3.1,8,3.1,11.1,0l46.9-46.9c1.4-1.4,3.4-1.4,4.8,0c1.4,1.4,1.4,3.4,0,4.8l-46.9,46.9
C752.1,279.6,743.3,279.6,737.6,274.2z" />
<path class="st2" d="M714,188c-1.4-1.4-1.4-3.4,0-4.8l23.6-23.6c1.4-1.4,3.4-1.4,4.8,0c1.4,1.4,1.4,3.4,0,4.8L718.8,188
C717.4,189.1,715.4,189.1,714,188z M702.3,199.7c-0.3-0.3-0.3-0.3-0.3-0.6c-0.3-0.3-0.3-0.3-0.3-0.6c0-0.3,0-0.3-0.3-0.6
c0-0.3,0-0.3,0-0.6s0-0.6,0-0.6c0-0.3,0-0.3,0.3-0.6c0-0.3,0.3-0.3,0.3-0.6s0.3,0,0.3-0.3c0.3-0.3,0.3-0.3,0.6-0.3
c0.3,0,0.3-0.3,0.6-0.3s0.6-0.3,0.6-0.3c0.3,0,0.3,0,0.6,0s0.6,0,0.6,0s0.3,0,0.6,0.3c0.3,0,0.3,0.3,0.6,0.3
c0.9,0.6,1.4,1.7,1.4,2.8c0,0.3,0,0.6,0,0.6c0,0.3,0,0.3-0.3,0.6c0,0.3-0.3,0.3-0.3,0.6s-0.6,0.3-0.6,0.3
c-0.6,0.6-1.4,1.1-2.3,0.9C703.7,200.8,702.9,200.2,702.3,199.7z" />
<path class="st3" d="M804.1,230.9l-78.2-78.2c-1.4-1.4-1.4-3.4,0-4.8l9.4-9.4c1.4-1.4,3.4-1.4,4.8,0l78.2,78.2
c1.4,1.4,1.4,3.4,0,4.8l-9.4,9.4C807.5,232.4,805.3,232.4,804.1,230.9z M732.7,150.4l73.7,73.7l4.8-4.8l-73.7-73.7L732.7,150.4z" />
<path class="st3" d="M758.6,166.7c-1.4-1.4-1.4-3.4,0-4.8l7.1-7.1l-7.1-7.1l-0.9,0.9c-1.4,1.4-3.4,1.4-4.8,0
c-1.4-1.4-1.4-3.4,0-4.8l3.1-3.1c1.4-1.4,3.4-1.4,4.8,0l11.7,11.7c1.4,1.4,1.4,3.4,0,4.8l-9.4,9.4
C762,168.1,760,168.1,758.6,166.7z M801.6,209.9c-1.4-1.4-1.4-3.4,0-4.8l7.1-7.1l-7.1-7.1l-0.9,0.9c-1.4,1.4-3.4,1.4-4.8,0
c-1.4-1.4-1.4-3.4,0-4.8l3.1-3.1c1.4-1.4,3.4-1.4,4.8,0l11.7,11.7c1.4,1.4,1.4,3.4,0,4.8l-9.4,9.4C805,211,803,211,801.6,209.9z" />
</g>
<g>
<path class="st7" d="M878.4,170.9l97.6,97.6l10.8-10.8c6-6,6-15.6,0-21.6l-38.7-38.7l-1.7-0.6c-6.5-2.3-8.5-10.5-3.7-15.6
l-3.4-3.4C921.6,160.4,901.1,161.3,878.4,170.9" />
<path class="st8" d="M909.4,164.4C909.4,164.1,909.4,164.1,909.4,164.4c-1.1-0.3-2-0.3-2.8-0.3c-8.8,0.3-18.2,2.8-28.2,6.8l0,0
l31,31C919.6,191.7,919.6,174.6,909.4,164.4 M991,246.6c-10.2-7.7-25-6.8-34.7,2.6l19.3,19.3l10.8-10.8
C989.6,254.6,991,250.6,991,246.6" />
<path class="st3" d="M972.8,271.6l-97.6-97.6c-1.1-1.1-1.4-2.3-1.1-3.7c0.3-1.4,1.1-2.6,2.6-3.1c27.9-11.9,48.6-9.4,65.7,8
l0.3,0.3c1.7,1.7,1.7,4.3,0,6c-1.7,1.7-4.3,1.7-6,0l-0.3-0.3c-13.4-13.4-29.3-16.2-50.6-8.2l89.9,89.9l8-8
c4.3-4.3,4.3-11.4,0-15.6l-34.4-34.7c-1.1-1.1-1.4-2.3-1.1-3.7l1.7-10.8c0.3-2.3,2.6-4,4.8-3.4c2.3,0.3,4,2.6,3.4,4.8l-1.4,8.5
l33.3,33c7.7,7.7,7.7,20.2,0,27.9l-10.8,10.8C977.4,273.3,974.5,273.3,972.8,271.6z" />
<path class="st2" d="M901.4,172.3c-6-6-15.6-6-21.6,0c-6,6-6,15.6,0,21.6l0,0c6,6,15.6,6,21.6,0
C907.4,188,907.4,178.3,901.4,172.3L901.4,172.3z M957.7,216.4c-3.4-3.4-8.8-3.4-11.9,0c-3.4,3.4-3.4,8.8,0,11.9
c3.4,3.1,8.8,3.4,11.9,0C960.9,225,960.9,219.6,957.7,216.4z" />
<path class="st3" d="M876.7,197.1c-7.7-7.7-7.7-20.2,0-27.9c7.7-7.7,20.2-7.7,27.9,0s7.7,20.2,0,27.9
C896.6,204.8,884.3,204.8,876.7,197.1z M898.3,175.2c-4.3-4.3-11.4-4.3-15.6,0c-4.3,4.3-4.3,11.4,0,15.6c4.3,4.3,11.4,4.3,15.6,0
C902.5,186.6,902.5,179.7,898.3,175.2z" />
<path class="st2" d="M986.2,257.1c-6-6-15.6-6-21.6,0c-6,6-6,15.6,0,21.6l0,0c6,6,15.6,6,21.6,0
C992.1,272.8,992.1,263.1,986.2,257.1L986.2,257.1z" />
<path class="st3" d="M961.4,281.9c-7.7-7.7-7.7-20.2,0-27.9c7.7-7.7,20.2-7.7,27.9,0c7.7,7.7,7.7,20.2,0,27.9
C981.6,289.5,969.1,289.5,961.4,281.9z M983.3,260.2c-4.3-4.3-11.4-4.3-15.6,0s-4.3,11.4,0,15.6c4.3,4.3,11.4,4.3,15.6,0
C987.6,271.6,987.6,264.5,983.3,260.2z" />
<path class="st3" d="M961.4,281.9l-98.1-98.1c-1.7-1.7-1.7-4.3,0-6s4.3-1.7,6,0l98.1,98.1c1.7,1.7,1.7,4.3,0,6
S963.1,283.6,961.4,281.9z M851.3,171.8c-0.9-0.9-1.4-2-1.1-3.1c0-1.1,0.3-2.3,1.1-3.1c0.9-0.9,2-1.4,3.1-1.1c2.3,0,4.3,2,4.3,4.3
c0,1.1-0.3,2.3-1.1,3.1c-0.9,0.9-2,1.4-3.1,1.1C853.3,173.2,852.2,172.6,851.3,171.8z" />
</g>
<g>
<path class="st3" d="M789.9,46.3c0-8.5,6.8-15.4,15.4-15.4c8.5,0,15.4,6.8,15.4,15.4s-6.8,15.4-15.4,15.4 M752.6,8.8
c0-8.5,6.8-15.4,15.4-15.4s15.4,6.8,15.4,15.4s-6.8,15.4-15.4,15.4" />
<path class="st11" d="M782.2,91.6l-59.7-59.7c-0.6-0.6-0.6-1.7,0-2.6l19.3-19.3c0.6-0.6,1.7-0.6,2.6,0l59.7,59.7
c0.6,0.6,0.6,1.7,0,2.6l-19.3,19.3C783.9,92.4,782.8,92.4,782.2,91.6z" />
<path class="st2" d="M725.6,26.4l-3.1,3.1c-0.6,0.6-0.6,1.7,0,2.6l5.1,5.1l40.4-3.4l0,0L757.2,23L725.6,26.4z M737.3,46.9
l-0.6-0.6l11.4,11.4l40.4-3.1l0,0l-10.8-10.8L737.3,46.9z M801,75.6l3.1-3.1c0.6-0.6,0.6-1.7,0-2.6l-5.1-5.1L758.6,68l0,0
l10.5,10.5L801,75.6z M772.6,4.2c-2.6-2.6-6.8-2.6-9.4,0s-2.6,6.8,0,9.4s6.8,2.6,9.4,0C775.1,11.1,775.1,6.8,772.6,4.2z
M810.1,41.5c-2.6-2.6-6.8-2.6-9.4,0c-2.6,2.6-2.6,6.8,0,9.4c2.6,2.6,6.8,2.6,9.4,0C812.7,48.3,812.7,44.1,810.1,41.5z" />
<path class="st3" d="M780.2,93.6l-59.7-59.7c-1.7-1.7-1.7-4.6,0-6.3l19.3-19.3c1.7-1.7,4.6-1.7,6.3,0L805.8,68
c1.7,1.7,1.7,4.6,0,6.3l-19.3,19.3C784.8,95.3,782.2,95.3,780.2,93.6z M725,30.7l58.6,58.6l17.9-17.9L743,12.8L725,30.7z
M712,64.8c-1.1-1.1-1.1-2.6,0-3.7l14.2-14.2c1.1-1.1,2.6-1.1,3.7,0c1.1,1.1,1.1,2.6,0,3.7l-14.2,14.2C714.5,66,713.1,66,712,64.8
z M749.2,102.4c-1.1-1.1-1.1-2.6,0-3.7l14.2-14.2c1.1-1.1,2.6-1.1,3.7,0c1.1,1.1,1.1,2.6,0,3.7l-14.2,14.2
C752.1,103.2,750.4,103.2,749.2,102.4z" />
</g>
<rect y="-34.4" class="st13" width="1024" height="568.9" />
</g>
<g id="Ebene_2">
<g>
<g>
<g>
<path class="st14"
d="M195.1,301.8l-4-35.1l-6.1,0.6l4,35.1L195.1,301.8z M216.9,299.2l-4-35.1l-6.1,0.6l4,35.1L216.9,299.2z" />
<path class="st15" d="M228.3,347c-1.9,2.3-3.4,3.8-3.6,4c-5.5,4.4-9.9,5.7-13.5,4c-6.3-3.2-5.9-15-5.7-16.3l4.4,0.2
c-0.2,3.4,0.4,10.6,3.4,12c1.7,0.8,4.6-0.2,8.5-3.4l0,0c0,0,12.3-12.3,9.7-22c-3-11.6,10.6-28.3,15-34l0.6-0.6l3.6,2.7l-0.6,0.8
c-13.7,16.9-15.2,25.6-14.2,30C238,333,232.3,342.1,228.3,347z" />
<path class="st15"
d="M181.6,301.3l1.7,14.4l13.3,8.9l18-1.9l11-11.6l-1.7-14.4L181.6,301.3z" />
<g>
<path class="st16" d="M214.3,322.7l-18,1.9l3.8,15.8l14.2-1.7V322.7L214.3,322.7z M227.2,284.6l1.5,13.1l-51.5,5.9l-1.5-13.1
L227.2,284.6z" />
</g>
</g>
<g>
<g>
<path class="st17" d="M267.6,140.1c-37,0-67,30-67,67c0,50.5,56.4,76.9,63.2,148.9c0.2,2.1,1.9,3.6,4,3.6c2.1,0,3.8-1.5,4-3.6
c6.8-72,63.2-98.4,63.2-148.9C334.5,169.9,304.5,140.1,267.6,140.1z" />
<path class="st18" d="M267.6,141.6c36.8,0,66.5,29.6,67,66.1c0-0.2,0-0.4,0-0.6c0-37-30-67-67-67s-67,29.8-67,67
c0,0.2,0,0.4,0,0.6C201,171.2,230.8,141.6,267.6,141.6L267.6,141.6z" />
<path class="st19" d="M271.6,354.4c-0.2,2.1-1.9,3.6-4,3.6s-3.8-1.5-4-3.6c-6.5-71.8-62.5-98.2-63-148.1c0,0.4,0,0.6,0,1.1
c0,50.5,56.4,76.9,63.2,148.9c0.2,2.1,1.9,3.6,4,3.6c2.1,0,3.8-1.5,4-3.6c6.8-72,63.2-98.4,63.2-148.9c0-0.4,0-0.6,0-1.1
C334.1,256.1,278.1,282.5,271.6,354.4L271.6,354.4z" />
</g>
<path class="st20"
d="M252.2,174.4v40.6h11v33.2l25.8-44.4h-14.8l14.8-29.6C289.1,174.4,252.2,174.4,252.2,174.4z" />
</g>
</g>
<text transform="matrix(1 0 0 1 417.7245 289.5375)" class="st2 st21 st22">EVMap</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

-23
View File
@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
viewBox="0 0 233.8 368.4" style="enable-background:new 0 0 233.8 368.4;" xml:space="preserve">
<style type="text/css">
.st0{fill:#DC4B3E;}
.st1{fill:#802C27;}
</style>
<g>
<g>
<g>
<path class="st0" d="M109.8,0h13.6c33.9,1.9,67.1,18.5,87.7,45.8c13.5,17.2,21,38.6,22.7,60.3v8.1c-0.8,42.1-27.7,76.6-51,109.4
c-26.2,37-50.4,77.3-57.1,122.9c-1.8,7.7,0.4,18.5-8.9,22c-2.2-1.7-4.7-3.1-6.2-5.4c-2.7-25.5-9.1-50.7-20-73.9
c-12.3-27.1-29.5-51.6-47-75.6C33,199,23,184.2,14.7,168.3c-13-23.8-17.9-51.9-12.5-78.6c4.4-21.1,15.4-40.6,30.6-55.7
C53.3,14,81.1,1.8,109.8,0z M107.2,74.1c-16.1,3.6-29.6,17.8-31,34.5c-1.7,15.5,7.4,31.3,21.3,38.1c15.1,8,34.9,5.5,47.5-6
c11.8-10.4,16-28.3,9.9-42.9C147.6,79.7,126,69.3,107.2,74.1z" />
</g>
</g>
<g>
<path class="st1" d="M107.2,74.1c18.9-4.8,40.4,5.5,47.7,23.7c6.1,14.5,1.9,32.5-9.9,42.9c-12.6,11.5-32.4,14-47.5,6
c-13.9-6.8-23-22.6-21.3-38.1C77.6,92,91.1,77.7,107.2,74.1z" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

-31
View File
@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
viewBox="0 0 233.8 368.4" style="enable-background:new 0 0 233.8 368.4;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{display:none;}
.st2{display:inline;fill:#802C27;}
.st3{fill:#808080;}
.st4{display:none;fill:#802C27;}
</style>
<g>
<g>
<g>
<path class="st0" d="M109.8,0h13.6c33.9,1.9,67.1,18.5,87.7,45.8c13.5,17.2,21,38.6,22.7,60.3v8.1c-0.8,42.1-27.7,76.6-51,109.4
c-26.2,37-50.4,77.3-57.1,122.9c-1.8,7.7,0.4,18.5-8.9,22c-2.2-1.7-4.7-3.1-6.2-5.4c-2.7-25.5-9.1-50.7-20-73.9
c-12.3-27.1-29.5-51.6-47-75.6C33,199,23,184.2,14.7,168.3c-13-23.8-17.9-51.9-12.5-78.6c4.4-21.1,15.4-40.6,30.6-55.7
C53.3,14,81.1,1.8,109.8,0z" />
</g>
</g>
<g class="st1">
<path class="st2" d="M107.2,74.1c18.9-4.8,40.4,5.5,47.7,23.7c6.1,14.5,1.9,32.5-9.9,42.9c-12.6,11.5-32.4,14-47.5,6
c-13.9-6.8-23-22.6-21.3-38.1C77.6,92,91.1,77.7,107.2,74.1z" />
</g>
</g>
<path class="st3" d="M90.9,57.3v68.2h18.6v55.8l43.4-74.4h-24.8l24.8-49.6H90.9z" />
<path class="st4" d="M159,85.3L159,85.3l-20.8-20.9l-5.9,5.9l11.8,11.8c-5.3,2-9,7.1-9,13.1c0,7.7,6.3,14,14,14c2,0,3.9-0.4,5.6-1.2
v40.4c0,3.1-2.5,5.6-5.6,5.6s-5.6-2.5-5.6-5.6v-25.2c0-6.2-5-11.2-11.2-11.2h-5.6V72.8c0-6.2-5-11.2-11.2-11.2H81.8
c-6.2,0-11.2,5-11.2,11.2v89.7h56.1v-42.1h8.4v28c0,7.7,6.3,14,14,14s14-6.3,14-14V95.2C163.1,91.3,161.6,87.8,159,85.3
M149.1,100.8c-3.1,0-5.6-2.5-5.6-5.6c0-3.1,2.5-5.6,5.6-5.6s5.6,2.5,5.6,5.6C154.7,98.3,152.2,100.8,149.1,100.8 M93.1,145.6v-25.2
H81.8l22.4-42.1v28h11.2L93.1,145.6z" />
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0"?>
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24px"
height="24px">
<path
d="M20.055,7.713c-0.677-0.842-1.673-1.41-2.764-1.615C16.773,3.971,14.877,3,13.045,3H7.057C6.425,3,5.878,3.409,5.689,4.009 c-0.015,0.04-0.026,0.082-0.036,0.125L3.034,16.262c-0.009,0.041-0.015,0.083-0.019,0.125C3.006,16.449,3,16.513,3,16.56 C3,17.354,3.648,18,4.444,18h2.316l-0.267,1.262c-0.008,0.04-0.014,0.081-0.018,0.121c-0.009,0.063-0.016,0.126-0.016,0.173 C6.461,20.353,7.109,21,7.905,21h3.259c0.056,0,0.111-0.005,0.166-0.015c0.549-0.063,1.011-0.437,1.191-0.963 c0.021-0.05,0.038-0.103,0.05-0.156L13.475,16h1.398c3.365,0,5.38-1.445,5.989-4.295C21.278,9.752,20.653,8.456,20.055,7.713z M5.137,16L7.512,5h5.533c0.293,0,1.5,0.061,2.078,1.013h-4.706c-0.626,0-1.17,0.401-1.363,0.99c-0.019,0.049-0.033,0.1-0.043,0.151 l-1.034,5.093L7.183,16H5.137z M18.906,11.287C18.5,13.188,17.293,14,14.873,14h-1.857c-0.823,0-1.338,0.652-1.405,1.198L10.721,19 H8.594l1.271-6h1.444c4.259,0,5.665-2.394,6.094-4.402c0.027-0.128,0.045-0.256,0.057-0.382c0.378,0.151,0.749,0.393,1.038,0.751 C18.971,9.557,19.108,10.337,18.906,11.287z" />
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 909 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 943 KiB

-1
View File
@@ -1 +0,0 @@
/build
-175
View File
@@ -1,175 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'androidx.navigation.safeargs.kotlin'
apply plugin: 'com.mikepenz.aboutlibraries.plugin'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "net.vonforst.evmap"
minSdkVersion 21
targetSdkVersion 29
versionCode 21
versionName "0.3.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
signingConfigs {
release
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug {
applicationIdSuffix ".debug"
debuggable true
}
}
flavorDimensions "dependencies"
productFlavors {
foss {
dimension "dependencies"
versionNameSuffix "-foss"
}
google {
dimension "dependencies"
versionNameSuffix "-google"
}
}
def isRunningOnTravis = System.getenv("CI") == "true"
if (isRunningOnTravis) {
// configure keystore
signingConfigs.release.storeFile = file("../_ci/keystore.jks")
signingConfigs.release.storePassword = System.getenv("keystore_password")
signingConfigs.release.keyAlias = System.getenv("keystore_alias")
signingConfigs.release.keyPassword = System.getenv("keystore_alias_password")
}
compileOptions {
coreLibraryDesugaringEnabled true
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
buildFeatures {
dataBinding = true
}
// add API keys from environment variable if not set in apikeys.xml
applicationVariants.all { variant ->
ext.env = System.getenv()
def goingelectricKey = env.GOINGELECTRIC_API_KEY
if (goingelectricKey != null) {
variant.resValue "string", "goingelectric_key", goingelectricKey
}
def googleMapsKey = env.GOOGLE_MAPS_API_KEY
if (googleMapsKey != null) {
variant.resValue "string", "google_maps_key", googleMapsKey
}
def mapboxKey = env.MAPBOX_API_KEY
if (mapboxKey != null) {
variant.resValue "string", "mapbox_key", mapboxKey
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.core:core-ktx:1.3.1'
implementation "androidx.activity:activity-ktx:1.1.0"
implementation "androidx.fragment:fragment-ktx:1.2.5"
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.core:core:1.3.1'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.preference:preference-ktx:1.1.1'
implementation 'com.google.android.material:material:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.browser:browser:1.2.0'
implementation 'com.github.johan12345:CustomBottomSheetBehavior:f69f532660'
implementation 'com.squareup.retrofit2:retrofit:2.7.2'
implementation 'com.squareup.retrofit2:converter-moshi:2.7.2'
implementation 'com.squareup.moshi:moshi-kotlin:1.9.2'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'com.github.MikeOrtiz:TouchImageView:2.3.3'
implementation "com.mikepenz:aboutlibraries-core:$about_libs_version"
implementation "com.mikepenz:aboutlibraries:$about_libs_version"
implementation 'com.airbnb.android:lottie:3.4.0'
implementation 'io.michaelrocks:bimap:1.0.2'
implementation 'com.mapzen.android:lost:3.0.2'
// AnyMaps
def anyMapsVersion = 'cc2a044656'
implementation "com.github.johan12345.AnyMaps:anymaps-base:$anyMapsVersion"
googleImplementation "com.github.johan12345.AnyMaps:anymaps-google:$anyMapsVersion"
implementation "com.github.johan12345.AnyMaps:anymaps-mapbox:$anyMapsVersion"
// Google Maps v3 Beta
googleImplementation 'com.google.android.libraries.maps:maps:3.1.0-beta'
googleImplementation name:'places-maps-sdk-3.1.0-beta', ext:'aar'
googleImplementation 'com.android.volley:volley:1.1.1'
googleImplementation 'com.google.android.gms:play-services-base:17.3.0'
googleImplementation 'com.google.android.gms:play-services-basement:17.3.0'
googleImplementation 'com.google.android.gms:play-services-gcm:17.0.0'
googleImplementation 'com.google.android.gms:play-services-location:17.0.0'
googleImplementation 'com.google.android.gms:play-services-tasks:17.1.0'
googleImplementation 'com.google.auto.value:auto-value-annotations:1.6.3'
googleImplementation 'com.google.code.gson:gson:2.8.6'
googleImplementation 'com.google.android.datatransport:transport-runtime:2.2.3'
googleImplementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
// Mapbox places (autocomplete)
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-places-v9:0.12.0'
// navigation library
def nav_version = "2.3.0"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
// viewmodel library
def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// room library
def room_version = "2.2.5"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
// billing library
def billing_version = "3.0.0"
googleImplementation "com.android.billingclient:billing:$billing_version"
googleImplementation "com.android.billingclient:billing-ktx:$billing_version"
// debug tools
implementation 'com.facebook.stetho:stetho:1.5.1'
implementation 'com.facebook.stetho:stetho-okhttp3:1.5.1'
testImplementation 'junit:junit:4.13'
testImplementation "com.squareup.okhttp3:mockwebserver:3.14.7"
//noinspection GradleDependency
testImplementation 'org.json:json:20080701'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.9.2"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.9'
}
Binary file not shown.
-21
View File
@@ -1,21 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -1,24 +0,0 @@
package com.johan.evmap
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.johan.evmap", appContext.packageName)
}
}
-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">EV Map (debug)</string>
</resources>
-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">EV Map (debug)</string>
</resources>
@@ -1,12 +0,0 @@
package net.vonforst.evmap
import android.app.Activity
import android.content.Context
fun init(context: Context) {
}
fun checkPlayServices(activity: Activity): Boolean {
return true
}
@@ -1,49 +0,0 @@
package net.vonforst.evmap.autocomplete
import android.content.Context
import android.content.Intent
import android.view.inputmethod.InputMethodManager
import androidx.fragment.app.Fragment
import com.car2go.maps.model.LatLng
import com.car2go.maps.model.LatLngBounds
import com.mapbox.geojson.BoundingBox
import com.mapbox.geojson.Point
import com.mapbox.mapboxsdk.plugins.places.autocomplete.PlaceAutocomplete
import com.mapbox.mapboxsdk.plugins.places.autocomplete.model.PlaceOptions
import net.vonforst.evmap.R
import net.vonforst.evmap.fragment.REQUEST_AUTOCOMPLETE
import net.vonforst.evmap.viewmodel.PlaceWithBounds
fun launchAutocomplete(fragment: Fragment) {
val placeOptions = PlaceOptions.builder()
.build(PlaceOptions.MODE_CARDS)
val intent = PlaceAutocomplete.IntentBuilder()
.accessToken(fragment.getString(R.string.mapbox_key))
.placeOptions(placeOptions)
.build(fragment.requireActivity())
.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
fragment.startActivityForResult(intent, REQUEST_AUTOCOMPLETE)
// show keyboard
val imm = fragment.requireContext()
.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.toggleSoftInput(0, 0)
}
fun handleAutocompleteResult(intent: Intent): PlaceWithBounds? {
val place = PlaceAutocomplete.getPlace(intent) ?: return null
val bbox = place.bbox()?.toLatLngBounds()
val center = place.center()!!.toLatLng()
return PlaceWithBounds(center, bbox)
}
private fun BoundingBox.toLatLngBounds(): LatLngBounds {
return LatLngBounds(
southwest().toLatLng(),
northeast().toLatLng()
)
}
private fun Point.toLatLng(): LatLng = LatLng(this.latitude(), this.longitude())
@@ -1,37 +0,0 @@
package net.vonforst.evmap.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import kotlinx.android.synthetic.foss.fragment_donate.*
import net.vonforst.evmap.MapsActivity
import net.vonforst.evmap.R
class DonateFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_donate, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
(requireActivity() as AppCompatActivity).setSupportActionBar(toolbar)
val navController = findNavController()
toolbar.setupWithNavController(
navController,
(requireActivity() as MapsActivity).appBarConfiguration
)
btnDonate.setOnClickListener {
(activity as? MapsActivity)?.openUrl(getString(R.string.paypal_link))
}
}
}
@@ -1,51 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/toolbar_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.appbar.AppBarLayout>
<Button
android:id="@+id/btnDonate"
style="@style/Widget.MaterialComponents.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="@string/donate_paypal"
app:icon="@drawable/ic_paypal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView20" />
<TextView
android:id="@+id/textView20"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/donations_info"
app:layout_constraintBottom_toTopOf="@+id/btnDonate"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar_container"
app:layout_constraintVertical_chainStyle="packed" />
</androidx.constraintlayout.widget.ConstraintLayout>
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="pref_map_provider_names">
<item>OpenStreetMap (Mapbox)</item>
</string-array>
<string name="donations_info" formatted="false">Findest du EVMap nützlich? Unterstütze die Weiterentwicklung der App mit einer Spende an den Entwickler.\n\nGoogle zieht von der Spende 30% Gebühren ab.</string>
<string name="donate_paypal">Mit PayPal spenden</string>
</resources>
-13
View File
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="pref_map_provider_names">
<item>OpenStreetMap (Mapbox)</item>
</string-array>
<string-array name="pref_map_provider_values" tranlatable="false">
<item>mapbox</item>
</string-array>
<string name="pref_map_provider_default" translatable="false">mapbox</string>
<string name="donations_info" formatted="false">Do you find EVMap useful? Support its development by sending a donation to the developer.</string>
<string name="donate_paypal">Donate with PayPal</string>
<string name="paypal_link" translatable="false">https://paypal.me/johan98</string>
</resources>
@@ -1,27 +0,0 @@
package net.vonforst.evmap
import android.app.Activity
import android.content.Context
import android.util.Log
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.libraries.places.api.Places
fun init(context: Context) {
Places.initialize(context, context.getString(R.string.google_maps_key));
}
fun checkPlayServices(activity: Activity): Boolean {
val request = 9000
val apiAvailability = GoogleApiAvailability.getInstance()
val resultCode = apiAvailability.isGooglePlayServicesAvailable(activity)
if (resultCode != ConnectionResult.SUCCESS) {
if (apiAvailability.isUserResolvableError(resultCode)) {
apiAvailability.getErrorDialog(activity, resultCode, request).show()
} else {
Log.d("EVMap", "This device is not supported.")
}
return false
}
return true
}
@@ -1,8 +0,0 @@
package net.vonforst.evmap.adapter
import net.vonforst.evmap.R
import net.vonforst.evmap.viewmodel.DonationItem
class DonationAdapter() : DataBindingAdapter<DonationItem>() {
override fun getItemViewType(position: Int): Int = R.layout.item_donation
}
@@ -1,33 +0,0 @@
package net.vonforst.evmap.autocomplete
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.view.inputmethod.InputMethodManager
import androidx.fragment.app.Fragment
import com.car2go.maps.google.adapter.AnyMapAdapter
import com.google.android.libraries.places.api.model.Place
import com.google.android.libraries.places.widget.Autocomplete
import com.google.android.libraries.places.widget.model.AutocompleteActivityMode
import net.vonforst.evmap.fragment.REQUEST_AUTOCOMPLETE
import net.vonforst.evmap.viewmodel.PlaceWithBounds
fun launchAutocomplete(fragment: Fragment) {
val fields = listOf(Place.Field.LAT_LNG, Place.Field.VIEWPORT)
val intent: Intent = Autocomplete.IntentBuilder(
AutocompleteActivityMode.OVERLAY, fields
)
.build(fragment.requireActivity())
.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
fragment.startActivityForResult(intent, REQUEST_AUTOCOMPLETE)
// show keyboard
val imm = fragment.requireContext()
.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.toggleSoftInput(0, 0)
}
fun handleAutocompleteResult(intent: Intent): PlaceWithBounds? {
val place = Autocomplete.getPlaceFromIntent(intent)
return PlaceWithBounds(AnyMapAdapter.adapt(place.latLng), AnyMapAdapter.adapt(place.viewport))
}
@@ -1,64 +0,0 @@
package net.vonforst.evmap.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar
import net.vonforst.evmap.MapsActivity
import net.vonforst.evmap.R
import net.vonforst.evmap.adapter.DonationAdapter
import net.vonforst.evmap.databinding.FragmentDonateBinding
import net.vonforst.evmap.viewmodel.DonateViewModel
class DonateFragment : Fragment() {
private lateinit var binding: FragmentDonateBinding
private val vm: DonateViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_donate, container, false)
binding.lifecycleOwner = this
binding.vm = vm
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val toolbar = view.findViewById(R.id.toolbar) as Toolbar
(requireActivity() as AppCompatActivity).setSupportActionBar(toolbar)
val navController = findNavController()
toolbar.setupWithNavController(
navController,
(requireActivity() as MapsActivity).appBarConfiguration
)
binding.productsList.apply {
adapter = DonationAdapter().apply {
onClickListener = {
vm.startPurchase(it, requireActivity())
}
}
layoutManager = LinearLayoutManager(context)
}
vm.purchaseSuccessful.observe(viewLifecycleOwner, Observer {
Snackbar.make(view, R.string.donation_successful, Snackbar.LENGTH_LONG).show()
})
vm.purchaseFailed.observe(viewLifecycleOwner, Observer {
Snackbar.make(view, R.string.donation_failed, Snackbar.LENGTH_LONG).show()
})
}
}
@@ -1,113 +0,0 @@
package net.vonforst.evmap.viewmodel
import android.app.Activity
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import com.android.billingclient.api.*
import net.vonforst.evmap.BuildConfig
import net.vonforst.evmap.adapter.Equatable
class DonateViewModel(application: Application) : AndroidViewModel(application),
PurchasesUpdatedListener {
private var billingClient = BillingClient.newBuilder(application)
.setListener(this)
.enablePendingPurchases()
.build()
init {
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingServiceDisconnected() {
}
override fun onBillingSetupFinished(p0: BillingResult) {
loadProducts()
// consume pending purchases
val purchases = billingClient.queryPurchases(BillingClient.SkuType.INAPP)
purchases.purchasesList?.forEach {
if (!it.isAcknowledged) {
consumePurchase(it.purchaseToken, false)
}
}
}
})
}
private fun loadProducts() {
val params = SkuDetailsParams.newBuilder()
.setType(BillingClient.SkuType.INAPP)
.setSkusList(
listOf(
"donate_1_eur", "donate_2_eur", "donate_5_eur", "donate_10_eur"
) +
if (BuildConfig.DEBUG) {
listOf(
"android.test.purchased", "android.test.canceled",
"android.test.item_unavailable"
)
} else {
emptyList()
}
)
.build()
billingClient.querySkuDetailsAsync(params) { result, details ->
if (result.responseCode == BillingClient.BillingResponseCode.OK && details != null) {
products.value = Resource.success(details
.sortedBy { it.priceAmountMicros }
.map { DonationItem(it) }
)
} else {
products.value = Resource.error(result.debugMessage, null)
}
}
}
val products: MutableLiveData<Resource<List<DonationItem>>> by lazy {
MutableLiveData<Resource<List<DonationItem>>>().apply {
value = Resource.loading(null)
}
}
val purchaseSuccessful = SingleLiveEvent<Nothing>()
val purchaseFailed = SingleLiveEvent<Nothing>()
override fun onPurchasesUpdated(result: BillingResult, purchases: List<Purchase>?) {
if (result.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
for (purchase in purchases) {
val purchaseToken = purchase.purchaseToken
consumePurchase(purchaseToken)
}
} else if (result.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
// Handle an error caused by a user cancelling the purchase flow.
} else {
purchaseFailed.call()
}
}
private fun consumePurchase(purchaseToken: String, showSuccess: Boolean = true) {
val params = ConsumeParams.newBuilder()
.setPurchaseToken(purchaseToken)
.build()
billingClient.consumeAsync(params) { _, _ ->
if (showSuccess) purchaseSuccessful.call()
}
}
fun startPurchase(it: DonationItem, activity: Activity) {
val flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(it.sku)
.build()
val response = billingClient.launchBillingFlow(activity, flowParams)
if (response.responseCode != BillingClient.BillingResponseCode.OK) {
purchaseFailed.call()
}
}
override fun onCleared() {
billingClient.endConnection()
}
}
data class DonationItem(val sku: SkuDetails) : Equatable
@@ -1,73 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="net.vonforst.evmap.viewmodel.DonateViewModel" />
<import type="net.vonforst.evmap.viewmodel.Status" />
<variable
name="vm"
type="DonateViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/toolbar_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.appbar.AppBarLayout>
<TextView
android:id="@+id/textView20"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/donations_info"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar_container" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/products_list"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="8dp"
app:data="@{vm.products.data}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView20"
tools:listitem="@layout/item_donation" />
<ProgressBar
android:id="@+id/progressBar3"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/products_list"
app:goneUnless="@{vm.products.status == Status.LOADING}" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
@@ -1,54 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item"
type="net.vonforst.evmap.viewmodel.DonationItem" />
</data>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:padding="16dp">
<TextView
android:id="@+id/textView15"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.sku.title}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/textView21"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Spende" />
<TextView
android:id="@+id/textView21"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.sku.price}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="?android:textColorSecondary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="1,00 €" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</layout>
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="pref_map_provider_names">
<item>Google Maps</item>
<item>OpenStreetMap (Mapbox)</item>
</string-array>
<string name="donations_info" formatted="false">Findest du EVMap nützlich? Unterstütze die Weiterentwicklung der App mit einer Spende an den Entwickler.\n\nGoogle zieht von der Spende 30% Gebühren ab.</string>
</resources>
-13
View File
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="pref_map_provider_names">
<item>Google Maps</item>
<item>OpenStreetMap (Mapbox)</item>
</string-array>
<string-array name="pref_map_provider_values" tranlatable="false">
<item>google</item>
<item>mapbox</item>
</string-array>
<string name="pref_map_provider_default" translatable="false">mapbox</string>
<string name="donations_info" formatted="false">Do you find EVMap useful? Support its development by sending a donation to the developer.\n\nGoogle takes 30% off every donation.</string>
</resources>
-44
View File
@@ -1,44 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.vonforst.evmap">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:name=".EvMapApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!--
The API key for Google Maps-based APIs is defined as a string resource.
(See the file "res/values/apikeys.xml").
Note that the API key is linked to the encryption key used to sign the APK.
You need a different API key for each encryption key, including the release key that is used to
sign the APK for publishing.
You can define the keys for the debug and release targets in src/debug/ and src/release/.
-->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="@string/google_maps_key" />
<meta-data
android:name="com.mapbox.ACCESS_TOKEN"
android:value="@string/mapbox_key" />
<activity
android:name=".MapsActivity"
android:label="@string/title_activity_maps"
android:theme="@style/AppTheme.LaunchScreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -1,32 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.maps.android.clustering;
import com.car2go.maps.model.LatLng;
import java.util.Collection;
/**
* A collection of ClusterItems that are nearby each other.
*/
public interface Cluster<T extends ClusterItem> {
LatLng getPosition();
Collection<T> getItems();
int getSize();
}
@@ -1,46 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.maps.android.clustering;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.car2go.maps.model.LatLng;
/**
* ClusterItem represents a marker on the map.
*/
public interface ClusterItem {
/**
* The position of this marker. This must always return the same value.
*/
@NonNull
LatLng getPosition();
/**
* The title of this marker.
*/
@Nullable
String getTitle();
/**
* The description of this marker.
*/
@Nullable
String getSnippet();
}
@@ -1,39 +0,0 @@
/*
* Copyright 2020 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.maps.android.clustering.algo;
import com.google.maps.android.clustering.ClusterItem;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Base Algorithm class that implements lock/unlock functionality.
*/
public abstract class AbstractAlgorithm<T extends ClusterItem> implements Algorithm<T> {
private final ReadWriteLock mLock = new ReentrantReadWriteLock();
@Override
public void lock() {
mLock.writeLock().lock();
}
@Override
public void unlock() {
mLock.writeLock().unlock();
}
}
@@ -1,85 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.maps.android.clustering.algo;
import com.google.maps.android.clustering.Cluster;
import com.google.maps.android.clustering.ClusterItem;
import java.util.Collection;
import java.util.Set;
/**
* Logic for computing clusters
*/
public interface Algorithm<T extends ClusterItem> {
/**
* Adds an item to the algorithm
*
* @param item the item to be added
* @return true if the algorithm contents changed as a result of the call
*/
boolean addItem(T item);
/**
* Adds a collection of items to the algorithm
*
* @param items the items to be added
* @return true if the algorithm contents changed as a result of the call
*/
boolean addItems(Collection<T> items);
void clearItems();
/**
* Removes an item from the algorithm
*
* @param item the item to be removed
* @return true if this algorithm contained the specified element (or equivalently, if this
* algorithm changed as a result of the call).
*/
boolean removeItem(T item);
/**
* Updates the provided item in the algorithm
*
* @param item the item to be updated
* @return true if the item existed in the algorithm and was updated, or false if the item did
* not exist in the algorithm and the algorithm contents remain unchanged.
*/
boolean updateItem(T item);
/**
* Removes a collection of items from the algorithm
*
* @param items the items to be removed
* @return true if this algorithm contents changed as a result of the call
*/
boolean removeItems(Collection<T> items);
Set<? extends Cluster<T>> getClusters(float zoom);
Collection<T> getItems();
void setMaxDistanceBetweenClusteredItems(int maxDistance);
int getMaxDistanceBetweenClusteredItems();
void lock();
void unlock();
}
@@ -1,314 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.maps.android.clustering.algo;
import com.car2go.maps.model.LatLng;
import com.google.maps.android.clustering.Cluster;
import com.google.maps.android.clustering.ClusterItem;
import com.google.maps.android.geometry.Bounds;
import com.google.maps.android.geometry.Point;
import com.google.maps.android.projection.SphericalMercatorProjection;
import com.google.maps.android.quadtree.PointQuadTree;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
/**
* A simple clustering algorithm with O(nlog n) performance. Resulting clusters are not
* hierarchical.
* <p/>
* High level algorithm:<br>
* 1. Iterate over items in the order they were added (candidate clusters).<br>
* 2. Create a cluster with the center of the item. <br>
* 3. Add all items that are within a certain distance to the cluster. <br>
* 4. Move any items out of an existing cluster if they are closer to another cluster. <br>
* 5. Remove those items from the list of candidate clusters.
* <p/>
* Clusters have the center of the first element (not the centroid of the items within it).
*/
public class NonHierarchicalDistanceBasedAlgorithm<T extends ClusterItem> extends AbstractAlgorithm<T> {
private static final int DEFAULT_MAX_DISTANCE_AT_ZOOM = 100; // essentially 100 dp.
private int mMaxDistance = DEFAULT_MAX_DISTANCE_AT_ZOOM;
/**
* Any modifications should be synchronized on mQuadTree.
*/
private final Collection<QuadItem<T>> mItems = new LinkedHashSet<>();
/**
* Any modifications should be synchronized on mQuadTree.
*/
private final PointQuadTree<QuadItem<T>> mQuadTree = new PointQuadTree<>(0, 1, 0, 1);
private static final SphericalMercatorProjection PROJECTION = new SphericalMercatorProjection(1);
/**
* Adds an item to the algorithm
*
* @param item the item to be added
* @return true if the algorithm contents changed as a result of the call
*/
@Override
public boolean addItem(T item) {
boolean result;
final QuadItem<T> quadItem = new QuadItem<>(item);
synchronized (mQuadTree) {
result = mItems.add(quadItem);
if (result) {
mQuadTree.add(quadItem);
}
}
return result;
}
/**
* Adds a collection of items to the algorithm
*
* @param items the items to be added
* @return true if the algorithm contents changed as a result of the call
*/
@Override
public boolean addItems(Collection<T> items) {
boolean result = false;
for (T item : items) {
boolean individualResult = addItem(item);
if (individualResult) {
result = true;
}
}
return result;
}
@Override
public void clearItems() {
synchronized (mQuadTree) {
mItems.clear();
mQuadTree.clear();
}
}
/**
* Removes an item from the algorithm
*
* @param item the item to be removed
* @return true if this algorithm contained the specified element (or equivalently, if this
* algorithm changed as a result of the call).
*/
@Override
public boolean removeItem(T item) {
boolean result;
// QuadItem delegates hashcode() and equals() to its item so,
// removing any QuadItem to that item will remove the item
final QuadItem<T> quadItem = new QuadItem<>(item);
synchronized (mQuadTree) {
result = mItems.remove(quadItem);
if (result) {
mQuadTree.remove(quadItem);
}
}
return result;
}
/**
* Removes a collection of items from the algorithm
*
* @param items the items to be removed
* @return true if this algorithm contents changed as a result of the call
*/
@Override
public boolean removeItems(Collection<T> items) {
boolean result = false;
synchronized (mQuadTree) {
for (T item : items) {
// QuadItem delegates hashcode() and equals() to its item so,
// removing any QuadItem to that item will remove the item
final QuadItem<T> quadItem = new QuadItem<>(item);
boolean individualResult = mItems.remove(quadItem);
if (individualResult) {
mQuadTree.remove(quadItem);
result = true;
}
}
}
return result;
}
/**
* Updates the provided item in the algorithm
*
* @param item the item to be updated
* @return true if the item existed in the algorithm and was updated, or false if the item did
* not exist in the algorithm and the algorithm contents remain unchanged.
*/
@Override
public boolean updateItem(T item) {
// TODO - Can this be optimized to update the item in-place if the location hasn't changed?
boolean result;
synchronized (mQuadTree) {
result = removeItem(item);
if (result) {
// Only add the item if it was removed (to help prevent accidental duplicates on map)
result = addItem(item);
}
}
return result;
}
@Override
public Set<? extends Cluster<T>> getClusters(float zoom) {
final int discreteZoom = (int) zoom;
final double zoomSpecificSpan = mMaxDistance / Math.pow(2, discreteZoom) / 256;
final Set<QuadItem<T>> visitedCandidates = new HashSet<>();
final Set<Cluster<T>> results = new HashSet<>();
final Map<QuadItem<T>, Double> distanceToCluster = new HashMap<>();
final Map<QuadItem<T>, StaticCluster<T>> itemToCluster = new HashMap<>();
synchronized (mQuadTree) {
for (QuadItem<T> candidate : getClusteringItems(mQuadTree, zoom)) {
if (visitedCandidates.contains(candidate)) {
// Candidate is already part of another cluster.
continue;
}
Bounds searchBounds = createBoundsFromSpan(candidate.getPoint(), zoomSpecificSpan);
Collection<QuadItem<T>> clusterItems;
clusterItems = mQuadTree.search(searchBounds);
if (clusterItems.size() == 1) {
// Only the current marker is in range. Just add the single item to the results.
results.add(candidate);
visitedCandidates.add(candidate);
distanceToCluster.put(candidate, 0d);
continue;
}
StaticCluster<T> cluster = new StaticCluster<>(candidate.mClusterItem.getPosition());
results.add(cluster);
for (QuadItem<T> clusterItem : clusterItems) {
Double existingDistance = distanceToCluster.get(clusterItem);
double distance = distanceSquared(clusterItem.getPoint(), candidate.getPoint());
if (existingDistance != null) {
// Item already belongs to another cluster. Check if it's closer to this cluster.
if (existingDistance < distance) {
continue;
}
// Move item to the closer cluster.
itemToCluster.get(clusterItem).remove(clusterItem.mClusterItem);
}
distanceToCluster.put(clusterItem, distance);
cluster.add(clusterItem.mClusterItem);
itemToCluster.put(clusterItem, cluster);
}
visitedCandidates.addAll(clusterItems);
}
}
return results;
}
protected Collection<QuadItem<T>> getClusteringItems(PointQuadTree<QuadItem<T>> quadTree, float zoom) {
return mItems;
}
@Override
public Collection<T> getItems() {
final Set<T> items = new LinkedHashSet<>();
synchronized (mQuadTree) {
for (QuadItem<T> quadItem : mItems) {
items.add(quadItem.mClusterItem);
}
}
return items;
}
@Override
public void setMaxDistanceBetweenClusteredItems(int maxDistance) {
mMaxDistance = maxDistance;
}
@Override
public int getMaxDistanceBetweenClusteredItems() {
return mMaxDistance;
}
private double distanceSquared(Point a, Point b) {
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
private Bounds createBoundsFromSpan(Point p, double span) {
// TODO: Use a span that takes into account the visual size of the marker, not just its
// LatLng.
double halfSpan = span / 2;
return new Bounds(
p.x - halfSpan, p.x + halfSpan,
p.y - halfSpan, p.y + halfSpan);
}
protected static class QuadItem<T extends ClusterItem> implements PointQuadTree.Item, Cluster<T> {
private final T mClusterItem;
private final Point mPoint;
private final LatLng mPosition;
private Set<T> singletonSet;
private QuadItem(T item) {
mClusterItem = item;
mPosition = item.getPosition();
mPoint = PROJECTION.toPoint(mPosition);
singletonSet = Collections.singleton(mClusterItem);
}
@Override
public Point getPoint() {
return mPoint;
}
@Override
public LatLng getPosition() {
return mPosition;
}
@Override
public Set<T> getItems() {
return singletonSet;
}
@Override
public int getSize() {
return 1;
}
@Override
public int hashCode() {
return mClusterItem.hashCode();
}
@Override
public boolean equals(Object other) {
if (!(other instanceof QuadItem<?>)) {
return false;
}
return ((QuadItem<?>) other).mClusterItem.equals(mClusterItem);
}
}
}
@@ -1,83 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.maps.android.clustering.algo;
import com.car2go.maps.model.LatLng;
import com.google.maps.android.clustering.Cluster;
import com.google.maps.android.clustering.ClusterItem;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* A cluster whose center is determined upon creation.
*/
public class StaticCluster<T extends ClusterItem> implements Cluster<T> {
private final LatLng mCenter;
private final List<T> mItems = new ArrayList<T>();
public StaticCluster(LatLng center) {
mCenter = center;
}
public boolean add(T t) {
return mItems.add(t);
}
@Override
public LatLng getPosition() {
return mCenter;
}
public boolean remove(T t) {
return mItems.remove(t);
}
@Override
public Collection<T> getItems() {
return mItems;
}
@Override
public int getSize() {
return mItems.size();
}
@Override
public String toString() {
return "StaticCluster{" +
"mCenter=" + mCenter +
", mItems.size=" + mItems.size() +
'}';
}
@Override
public int hashCode() {
return mCenter.hashCode() + mItems.hashCode();
}
@Override
public boolean equals(Object other) {
if (!(other instanceof StaticCluster<?>)) {
return false;
}
return ((StaticCluster<?>) other).mCenter.equals(mCenter)
&& ((StaticCluster<?>) other).mItems.equals(mItems);
}
}
@@ -1,61 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.maps.android.geometry;
/**
* Represents an area in the cartesian plane.
*/
public class Bounds {
public final double minX;
public final double minY;
public final double maxX;
public final double maxY;
public final double midX;
public final double midY;
public Bounds(double minX, double maxX, double minY, double maxY) {
this.minX = minX;
this.minY = minY;
this.maxX = maxX;
this.maxY = maxY;
midX = (minX + maxX) / 2;
midY = (minY + maxY) / 2;
}
public boolean contains(double x, double y) {
return minX <= x && x <= maxX && minY <= y && y <= maxY;
}
public boolean contains(Point point) {
return contains(point.x, point.y);
}
public boolean intersects(double minX, double maxX, double minY, double maxY) {
return minX < this.maxX && this.minX < maxX && minY < this.maxY && this.minY < maxY;
}
public boolean intersects(Bounds bounds) {
return intersects(bounds.minX, bounds.maxX, bounds.minY, bounds.maxY);
}
public boolean contains(Bounds bounds) {
return bounds.minX >= minX && bounds.maxX <= maxX && bounds.minY >= minY && bounds.maxY <= maxY;
}
}
@@ -1,35 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.maps.android.geometry;
public class Point {
public final double x;
public final double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
'}';
}
}
@@ -1,27 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.maps.android.projection;
/**
* @deprecated since 0.2. Use {@link com.google.maps.android.geometry.Point} instead.
*/
@Deprecated
public class Point extends com.google.maps.android.geometry.Point {
public Point(double x, double y) {
super(x, y);
}
}
@@ -1,46 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.maps.android.projection;
import com.car2go.maps.model.LatLng;
public class SphericalMercatorProjection {
final double mWorldWidth;
public SphericalMercatorProjection(final double worldWidth) {
mWorldWidth = worldWidth;
}
@SuppressWarnings("deprecation")
public Point toPoint(final LatLng latLng) {
final double x = latLng.longitude / 360 + .5;
final double siny = Math.sin(Math.toRadians(latLng.latitude));
final double y = 0.5 * Math.log((1 + siny) / (1 - siny)) / -(2 * Math.PI) + .5;
return new Point(x * mWorldWidth, y * mWorldWidth);
}
public LatLng toLatLng(com.google.maps.android.geometry.Point point) {
final double x = point.x / mWorldWidth - 0.5;
final double lng = x * 360;
double y = .5 - (point.y / mWorldWidth);
final double lat = 90 - Math.toDegrees(Math.atan(Math.exp(-y * 2 * Math.PI)) * 2);
return new LatLng(lat, lng);
}
}
@@ -1,226 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.maps.android.quadtree;
import com.google.maps.android.geometry.Bounds;
import com.google.maps.android.geometry.Point;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* A quad tree which tracks items with a Point geometry.
* See http://en.wikipedia.org/wiki/Quadtree for details on the data structure.
* This class is not thread safe.
*/
public class PointQuadTree<T extends PointQuadTree.Item> {
public interface Item {
Point getPoint();
}
/**
* The bounds of this quad.
*/
private final Bounds mBounds;
/**
* The depth of this quad in the tree.
*/
private final int mDepth;
/**
* Maximum number of elements to store in a quad before splitting.
*/
private final static int MAX_ELEMENTS = 50;
/**
* The elements inside this quad, if any.
*/
private Set<T> mItems;
/**
* Maximum depth.
*/
private final static int MAX_DEPTH = 40;
/**
* Child quads.
*/
private List<PointQuadTree<T>> mChildren = null;
/**
* Creates a new quad tree with specified bounds.
*
* @param minX
* @param maxX
* @param minY
* @param maxY
*/
public PointQuadTree(double minX, double maxX, double minY, double maxY) {
this(new Bounds(minX, maxX, minY, maxY));
}
public PointQuadTree(Bounds bounds) {
this(bounds, 0);
}
private PointQuadTree(double minX, double maxX, double minY, double maxY, int depth) {
this(new Bounds(minX, maxX, minY, maxY), depth);
}
private PointQuadTree(Bounds bounds, int depth) {
mBounds = bounds;
mDepth = depth;
}
/**
* Insert an item.
*/
public void add(T item) {
Point point = item.getPoint();
if (this.mBounds.contains(point.x, point.y)) {
insert(point.x, point.y, item);
}
}
private void insert(double x, double y, T item) {
if (this.mChildren != null) {
if (y < mBounds.midY) {
if (x < mBounds.midX) { // top left
mChildren.get(0).insert(x, y, item);
} else { // top right
mChildren.get(1).insert(x, y, item);
}
} else {
if (x < mBounds.midX) { // bottom left
mChildren.get(2).insert(x, y, item);
} else {
mChildren.get(3).insert(x, y, item);
}
}
return;
}
if (mItems == null) {
mItems = new LinkedHashSet<>();
}
mItems.add(item);
if (mItems.size() > MAX_ELEMENTS && mDepth < MAX_DEPTH) {
split();
}
}
/**
* Split this quad.
*/
private void split() {
mChildren = new ArrayList<PointQuadTree<T>>(4);
mChildren.add(new PointQuadTree<T>(mBounds.minX, mBounds.midX, mBounds.minY, mBounds.midY, mDepth + 1));
mChildren.add(new PointQuadTree<T>(mBounds.midX, mBounds.maxX, mBounds.minY, mBounds.midY, mDepth + 1));
mChildren.add(new PointQuadTree<T>(mBounds.minX, mBounds.midX, mBounds.midY, mBounds.maxY, mDepth + 1));
mChildren.add(new PointQuadTree<T>(mBounds.midX, mBounds.maxX, mBounds.midY, mBounds.maxY, mDepth + 1));
Set<T> items = mItems;
mItems = null;
for (T item : items) {
// re-insert items into child quads.
insert(item.getPoint().x, item.getPoint().y, item);
}
}
/**
* Remove the given item from the set.
*
* @return whether the item was removed.
*/
public boolean remove(T item) {
Point point = item.getPoint();
if (this.mBounds.contains(point.x, point.y)) {
return remove(point.x, point.y, item);
} else {
return false;
}
}
private boolean remove(double x, double y, T item) {
if (this.mChildren != null) {
if (y < mBounds.midY) {
if (x < mBounds.midX) { // top left
return mChildren.get(0).remove(x, y, item);
} else { // top right
return mChildren.get(1).remove(x, y, item);
}
} else {
if (x < mBounds.midX) { // bottom left
return mChildren.get(2).remove(x, y, item);
} else {
return mChildren.get(3).remove(x, y, item);
}
}
} else {
if (mItems == null) {
return false;
} else {
return mItems.remove(item);
}
}
}
/**
* Removes all points from the quadTree
*/
public void clear() {
mChildren = null;
if (mItems != null) {
mItems.clear();
}
}
/**
* Search for all items within a given bounds.
*/
public Collection<T> search(Bounds searchBounds) {
final List<T> results = new ArrayList<T>();
search(searchBounds, results);
return results;
}
private void search(Bounds searchBounds, Collection<T> results) {
if (!mBounds.intersects(searchBounds)) {
return;
}
if (this.mChildren != null) {
for (PointQuadTree<T> quad : mChildren) {
quad.search(searchBounds, results);
}
} else if (mItems != null) {
if (searchBounds.contains(mBounds)) {
results.addAll(mItems);
} else {
for (T item : mItems) {
if (searchBounds.contains(item.getPoint())) {
results.add(item);
}
}
}
}
}
}
@@ -1,268 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.maps.android.ui;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import net.vonforst.evmap.R;
/**
* IconGenerator generates icons that contain text (or custom content) within an info
* window-like shape.
* <p/>
* The icon {@link Bitmap}s generated by the factory should be used in conjunction with a
* BitmapDescriptorFactory.
* <p/>
* This class is not thread safe.
*/
public class IconGenerator {
private final Context mContext;
private ViewGroup mContainer;
private RotationLayout mRotationLayout;
private TextView mTextView;
private View mContentView;
private int mRotation;
private float mAnchorU = 0.5f;
private float mAnchorV = 1f;
/**
* Creates a new IconGenerator with the default style.
*/
public IconGenerator(Context context) {
mContext = context;
mContainer = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.amu_text_bubble, null);
mRotationLayout = (RotationLayout) mContainer.getChildAt(0);
mContentView = mTextView = (TextView) mRotationLayout.findViewById(R.id.amu_text);
}
/**
* Sets the text content, then creates an icon with the current style.
*
* @param text the text content to display inside the icon.
*/
public Bitmap makeIcon(CharSequence text) {
if (mTextView != null) {
mTextView.setText(text);
}
return makeIcon();
}
/**
* Creates an icon with the current content and style.
* <p/>
* This method is useful if a custom view has previously been set, or if text content is not
* applicable.
*/
public Bitmap makeIcon() {
int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
mContainer.measure(measureSpec, measureSpec);
int measuredWidth = mContainer.getMeasuredWidth();
int measuredHeight = mContainer.getMeasuredHeight();
mContainer.layout(0, 0, measuredWidth, measuredHeight);
if (mRotation == 1 || mRotation == 3) {
measuredHeight = mContainer.getMeasuredWidth();
measuredWidth = mContainer.getMeasuredHeight();
}
Bitmap r = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
r.eraseColor(Color.TRANSPARENT);
Canvas canvas = new Canvas(r);
switch (mRotation) {
case 0:
// do nothing
break;
case 1:
canvas.translate(measuredWidth, 0);
canvas.rotate(90);
break;
case 2:
canvas.rotate(180, measuredWidth / 2, measuredHeight / 2);
break;
case 3:
canvas.translate(0, measuredHeight);
canvas.rotate(270);
break;
}
mContainer.draw(canvas);
return r;
}
/**
* Sets the child view for the icon.
* <p/>
* If the view contains a {@link TextView} with the id "text", operations such as {@link
* #setTextAppearance} and {@link #makeIcon(CharSequence)} will operate upon that {@link TextView}.
*/
public void setContentView(View contentView) {
mRotationLayout.removeAllViews();
mRotationLayout.addView(contentView);
mContentView = contentView;
final View view = mRotationLayout.findViewById(R.id.amu_text);
mTextView = view instanceof TextView ? (TextView) view : null;
}
/**
* Rotates the contents of the icon.
*
* @param degrees the amount the contents should be rotated, as a multiple of 90 degrees.
*/
public void setContentRotation(int degrees) {
mRotationLayout.setViewRotation(degrees);
}
/**
* Rotates the icon.
*
* @param degrees the amount the icon should be rotated, as a multiple of 90 degrees.
*/
public void setRotation(int degrees) {
mRotation = ((degrees + 360) % 360) / 90;
}
/**
* @return u coordinate of the anchor, with rotation applied.
*/
public float getAnchorU() {
return rotateAnchor(mAnchorU, mAnchorV);
}
/**
* @return v coordinate of the anchor, with rotation applied.
*/
public float getAnchorV() {
return rotateAnchor(mAnchorV, mAnchorU);
}
/**
* Rotates the anchor around (u, v) = (0, 0).
*/
private float rotateAnchor(float u, float v) {
switch (mRotation) {
case 0:
return u;
case 1:
return 1 - v;
case 2:
return 1 - u;
case 3:
return v;
}
throw new IllegalStateException();
}
/**
* Sets the text color, size, style, hint color, and highlight color from the specified
* <code>TextAppearance</code> resource.
*
* @param resid the identifier of the resource.
*/
public void setTextAppearance(Context context, int resid) {
if (mTextView != null) {
mTextView.setTextAppearance(context, resid);
}
}
/**
* Sets the text color, size, style, hint color, and highlight color from the specified
* <code>TextAppearance</code> resource.
*
* @param resid the identifier of the resource.
*/
public void setTextAppearance(int resid) {
setTextAppearance(mContext, resid);
}
/**
* Set the background to a given Drawable, or remove the background.
*
* @param background the Drawable to use as the background, or null to remove the background.
*/
@SuppressWarnings("deprecation")
// View#setBackgroundDrawable is compatible with pre-API level 16 (Jelly Bean).
public void setBackground(Drawable background) {
mContainer.setBackgroundDrawable(background);
// Force setting of padding.
// setBackgroundDrawable does not call setPadding if the background has 0 padding.
if (background != null) {
Rect rect = new Rect();
background.getPadding(rect);
mContainer.setPadding(rect.left, rect.top, rect.right, rect.bottom);
} else {
mContainer.setPadding(0, 0, 0, 0);
}
}
/**
* Sets the padding of the content view. The default padding of the content view (i.e. text
* view) is 5dp top/bottom and 10dp left/right.
*
* @param left the left padding in pixels.
* @param top the top padding in pixels.
* @param right the right padding in pixels.
* @param bottom the bottom padding in pixels.
*/
public void setContentPadding(int left, int top, int right, int bottom) {
mContentView.setPadding(left, top, right, bottom);
}
public static final int STYLE_DEFAULT = 1;
public static final int STYLE_WHITE = 2;
public static final int STYLE_RED = 3;
public static final int STYLE_BLUE = 4;
public static final int STYLE_GREEN = 5;
public static final int STYLE_PURPLE = 6;
public static final int STYLE_ORANGE = 7;
private static int getStyleColor(int style) {
switch (style) {
default:
case STYLE_DEFAULT:
case STYLE_WHITE:
return 0xffffffff;
case STYLE_RED:
return 0xffcc0000;
case STYLE_BLUE:
return 0xff0099cc;
case STYLE_GREEN:
return 0xff669900;
case STYLE_PURPLE:
return 0xff9933cc;
case STYLE_ORANGE:
return 0xffff8800;
}
}
}
@@ -1,83 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.maps.android.ui;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.FrameLayout;
/**
* RotationLayout rotates the contents of the layout by multiples of 90 degrees.
* <p/>
* May not work with padding.
*/
public class RotationLayout extends FrameLayout {
private int mRotation;
public RotationLayout(Context context) {
super(context);
}
public RotationLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RotationLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mRotation == 1 || mRotation == 3) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
/**
* @param degrees the rotation, in degrees.
*/
public void setViewRotation(int degrees) {
mRotation = ((degrees + 360) % 360) / 90;
}
@Override
public void dispatchDraw(Canvas canvas) {
if (mRotation == 0) {
super.dispatchDraw(canvas);
return;
}
if (mRotation == 1) {
canvas.translate(getWidth(), 0);
canvas.rotate(90, getWidth() / 2, 0);
canvas.translate(getHeight() / 2, getWidth() / 2);
} else if (mRotation == 2) {
canvas.rotate(180, getWidth() / 2, getHeight() / 2);
} else {
canvas.translate(0, getHeight());
canvas.rotate(270, getWidth() / 2, 0);
canvas.translate(getHeight() / 2, -getWidth() / 2);
}
super.dispatchDraw(canvas);
}
}
@@ -1,62 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.maps.android.ui;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import androidx.appcompat.widget.AppCompatTextView;
public class SquareTextView extends AppCompatTextView {
private int mOffsetTop = 0;
private int mOffsetLeft = 0;
public SquareTextView(Context context) {
super(context);
}
public SquareTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquareTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
int dimension = Math.max(width, height);
if (width > height) {
mOffsetTop = width - height;
mOffsetLeft = 0;
} else {
mOffsetTop = 0;
mOffsetLeft = height - width;
}
setMeasuredDimension(dimension, dimension);
}
@Override
public void draw(Canvas canvas) {
canvas.translate(mOffsetLeft / 2, mOffsetTop / 2);
super.draw(canvas);
}
}
@@ -1,15 +0,0 @@
package net.vonforst.evmap
import android.app.Application
import com.facebook.stetho.Stetho
import net.vonforst.evmap.storage.PreferenceDataSource
import net.vonforst.evmap.ui.updateNightMode
class EvMapApplication : Application() {
override fun onCreate() {
super.onCreate()
updateNightMode(PreferenceDataSource(this))
Stetho.initializeWithDefaults(this);
init(applicationContext)
}
}
@@ -1,110 +0,0 @@
package net.vonforst.evmap
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.content.ContextCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.navigation.NavController
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupWithNavController
import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.Snackbar
import net.vonforst.evmap.api.goingelectric.ChargeLocation
import net.vonforst.evmap.storage.PreferenceDataSource
import net.vonforst.evmap.utils.LocaleContextWrapper
const val REQUEST_LOCATION_PERMISSION = 1
class MapsActivity : AppCompatActivity() {
interface FragmentCallback {
fun getRootView(): View
}
private var reenterState: Bundle? = null
private lateinit var navController: NavController
lateinit var appBarConfiguration: AppBarConfiguration
var fragmentCallback: FragmentCallback? = null
private lateinit var prefs: PreferenceDataSource
override fun attachBaseContext(newBase: Context) {
return super.attachBaseContext(
LocaleContextWrapper.wrap(
newBase, PreferenceDataSource(newBase).language
)
);
}
override fun onCreate(savedInstanceState: Bundle?) {
// set theme to AppTheme to end launch screen
setTheme(R.style.AppTheme)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_maps)
navController = findNavController(R.id.nav_host_fragment)
appBarConfiguration = AppBarConfiguration(
setOf(
R.id.map,
R.id.favs,
R.id.about
),
findViewById<DrawerLayout>(R.id.drawer_layout)
)
findViewById<NavigationView>(R.id.nav_view).setupWithNavController(navController)
prefs = PreferenceDataSource(this)
checkPlayServices(this)
}
fun navigateTo(charger: ChargeLocation) {
// google maps navigation
val coord = charger.coordinates
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("google.navigation:q=${coord.lat},${coord.lng}")
if (prefs.navigateUseMaps && intent.resolveActivity(packageManager) != null) {
startActivity(intent);
} else {
// fallback: generic geo intent
showLocation(charger)
}
}
fun showLocation(charger: ChargeLocation) {
val coord = charger.coordinates
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("geo:0,0?q=${coord.lat},${coord.lng}(${charger.name})")
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent);
} else {
val cb = fragmentCallback ?: return
Snackbar.make(
cb.getRootView(),
R.string.no_maps_app_found,
Snackbar.LENGTH_SHORT
)
}
}
fun openUrl(url: String) {
val intent = CustomTabsIntent.Builder()
.setToolbarColor(ContextCompat.getColor(this, R.color.colorPrimary))
.build()
intent.launchUrl(this, Uri.parse(url))
}
fun shareUrl(url: String) {
val intent = Intent(Intent.ACTION_SEND).apply {
setType("text/plain")
putExtra(Intent.EXTRA_TEXT, url)
}
startActivity(intent)
}
}
@@ -1,54 +0,0 @@
package net.vonforst.evmap
import android.graphics.Typeface
import android.os.Bundle
import android.text.*
import android.text.style.StyleSpan
fun Bundle.optDouble(name: String): Double? {
if (!this.containsKey(name)) return null
val dbl = this.getDouble(name, Double.NaN)
return if (dbl.isNaN()) null else dbl
}
fun Bundle.optLong(name: String): Long? {
if (!this.containsKey(name)) return null
val lng = this.getLong(name, Long.MIN_VALUE)
return if (lng == Long.MIN_VALUE) null else lng
}
fun <T> Iterable<T>.joinToSpannedString(
separator: CharSequence = ", ",
prefix: CharSequence = "",
postfix: CharSequence = "",
limit: Int = -1,
truncated: CharSequence = "...",
transform: ((T) -> CharSequence)? = null
): CharSequence {
return SpannedString(
joinTo(
SpannableStringBuilder(),
separator,
prefix,
postfix,
limit,
truncated,
transform
)
)
}
operator fun CharSequence.plus(other: CharSequence): CharSequence {
return TextUtils.concat(this, other)
}
fun String.bold(): CharSequence {
return SpannableString(this).apply {
setSpan(
StyleSpan(Typeface.BOLD), 0, this.length,
Spannable.SPAN_INCLUSIVE_EXCLUSIVE
)
}
}
@@ -1,91 +0,0 @@
package net.vonforst.evmap.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import net.vonforst.evmap.BR
import net.vonforst.evmap.R
import net.vonforst.evmap.api.availability.ChargepointStatus
import net.vonforst.evmap.api.goingelectric.Chargepoint
import net.vonforst.evmap.viewmodel.FavoritesViewModel
interface Equatable {
override fun equals(other: Any?): Boolean;
}
abstract class DataBindingAdapter<T : Equatable>(getKey: ((T) -> Any)? = null) :
ListAdapter<T, DataBindingAdapter.ViewHolder<T>>(DiffCallback(getKey)) {
var onClickListener: ((T) -> Unit)? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<T> {
val layoutInflater = LayoutInflater.from(parent.context)
val binding =
DataBindingUtil.inflate<ViewDataBinding>(layoutInflater, viewType, parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder<T>, position: Int) =
bind(holder, getItem(position))
class ViewHolder<T>(val binding: ViewDataBinding) :
RecyclerView.ViewHolder(binding.root) {
}
open fun bind(holder: ViewHolder<T>, item: T) {
holder.binding.setVariable(BR.item, item)
holder.binding.executePendingBindings()
if (onClickListener != null) {
holder.binding.root.setOnClickListener {
val listener = onClickListener ?: return@setOnClickListener
listener(item)
}
}
}
class DiffCallback<T : Equatable>(val getKey: ((T) -> Any)?) : DiffUtil.ItemCallback<T>() {
override fun areItemsTheSame(oldItem: T, newItem: T): Boolean = if (getKey != null) {
(getKey)(oldItem) == (getKey)(newItem)
} else {
oldItem === newItem
}
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean = oldItem == newItem
}
}
fun chargepointWithAvailability(
chargepoints: Iterable<Chargepoint>?,
availability: Map<Chargepoint, List<ChargepointStatus>>?
): List<ConnectorAdapter.ChargepointWithAvailability>? {
return chargepoints?.map {
val status = availability?.get(it)
ConnectorAdapter.ChargepointWithAvailability(it, status)
}
}
class ConnectorAdapter : DataBindingAdapter<ConnectorAdapter.ChargepointWithAvailability>() {
data class ChargepointWithAvailability(
val chargepoint: Chargepoint,
val status: List<ChargepointStatus>?
) :
Equatable
override fun getItemViewType(position: Int): Int = R.layout.item_connector
}
class FavoritesAdapter(val vm: FavoritesViewModel) :
DataBindingAdapter<FavoritesViewModel.FavoritesListItem>() {
init {
setHasStableIds(true)
}
override fun getItemViewType(position: Int): Int = R.layout.item_favorite
override fun getItemId(position: Int): Long = getItem(position).charger.id
}
@@ -1,139 +0,0 @@
package net.vonforst.evmap.adapter
import android.content.Context
import androidx.core.text.HtmlCompat
import net.vonforst.evmap.R
import net.vonforst.evmap.api.goingelectric.ChargeCard
import net.vonforst.evmap.api.goingelectric.ChargeCardId
import net.vonforst.evmap.api.goingelectric.ChargeLocation
import net.vonforst.evmap.api.goingelectric.OpeningHoursDays
import net.vonforst.evmap.bold
import net.vonforst.evmap.joinToSpannedString
import net.vonforst.evmap.plus
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
class DetailsAdapter : DataBindingAdapter<DetailsAdapter.Detail>() {
data class Detail(
val icon: Int,
val contentDescription: Int,
val text: CharSequence,
val detailText: CharSequence? = null,
val links: Boolean = true,
val clickable: Boolean = false,
val hoursDays: OpeningHoursDays? = null
) : Equatable
override fun getItemViewType(position: Int): Int {
val item = getItem(position)
if (item.hoursDays != null) {
return R.layout.item_detail_openinghours
} else {
return R.layout.item_detail
}
}
}
fun buildDetails(
loc: ChargeLocation?,
chargeCards: Map<Long, ChargeCard>?,
filteredChargeCards: Set<Long>?,
ctx: Context
): List<DetailsAdapter.Detail> {
if (loc == null) return emptyList()
return listOfNotNull(
DetailsAdapter.Detail(
R.drawable.ic_address,
R.string.address,
loc.address.toString(),
loc.locationDescription
),
if (loc.operator != null) DetailsAdapter.Detail(
R.drawable.ic_operator,
R.string.operator,
loc.operator
) else null,
if (loc.network != null) DetailsAdapter.Detail(
R.drawable.ic_network,
R.string.network,
loc.network
) else null,
if (loc.faultReport != null) DetailsAdapter.Detail(
R.drawable.ic_fault_report,
R.string.fault_report,
loc.faultReport.created?.let {
ctx.getString(
R.string.fault_report_date,
loc.faultReport.created
.atZone(ZoneId.systemDefault())
.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT))
)
} ?: "",
loc.faultReport.description?.let {
HtmlCompat.fromHtml(it, HtmlCompat.FROM_HTML_MODE_LEGACY)
} ?: "",
clickable = true
) else null,
if (loc.openinghours != null && !loc.openinghours.isEmpty) DetailsAdapter.Detail(
R.drawable.ic_hours,
R.string.hours,
loc.openinghours.getStatusText(ctx),
loc.openinghours.description,
hoursDays = loc.openinghours.days
) else null,
if (loc.cost != null) DetailsAdapter.Detail(
R.drawable.ic_cost,
R.string.cost,
loc.cost.getStatusText(ctx),
loc.cost.descriptionLong ?: loc.cost.descriptionShort
)
else null,
if (loc.chargecards != null && loc.chargecards.isNotEmpty()) DetailsAdapter.Detail(
R.drawable.ic_payment,
R.string.charge_cards,
ctx.resources.getQuantityString(
R.plurals.charge_cards_compatible_num,
loc.chargecards.size, loc.chargecards.size
),
formatChargeCards(loc.chargecards, chargeCards, filteredChargeCards, ctx),
clickable = true
) else null,
DetailsAdapter.Detail(
R.drawable.ic_location,
R.string.coordinates,
loc.coordinates.formatDMS(),
loc.coordinates.formatDecimal(),
links = false,
clickable = true
)
)
}
fun formatChargeCards(
chargecards: List<ChargeCardId>,
chargecardData: Map<Long, ChargeCard>?,
filteredChargeCards: Set<Long>?,
ctx: Context
): CharSequence {
if (chargecardData == null) return ""
val maxItems = 5
var result = chargecards
.sortedByDescending { filteredChargeCards?.contains(it.id) }
.take(maxItems)
.mapNotNull {
val name = chargecardData[it.id]?.name ?: return@mapNotNull null
if (filteredChargeCards?.contains(it.id) == true) {
name.bold()
} else {
name
}
}.joinToSpannedString()
if (chargecards.size > maxItems) {
result += " " + ctx.getString(R.string.and_n_others, chargecards.size - maxItems)
}
return result
}
@@ -1,227 +0,0 @@
package net.vonforst.evmap.adapter
import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.children
import androidx.databinding.Observable
import com.google.android.material.chip.Chip
import net.vonforst.evmap.BR
import net.vonforst.evmap.R
import net.vonforst.evmap.databinding.ItemFilterMultipleChoiceBinding
import net.vonforst.evmap.databinding.ItemFilterMultipleChoiceLargeBinding
import net.vonforst.evmap.databinding.ItemFilterSliderBinding
import net.vonforst.evmap.fragment.MultiSelectDialog
import net.vonforst.evmap.viewmodel.*
import kotlin.math.max
class FiltersAdapter : DataBindingAdapter<FilterWithValue<FilterValue>>() {
init {
setHasStableIds(true)
}
val itemids = mutableMapOf<String, Long>()
var maxId = 0L
override fun getItemViewType(position: Int): Int =
when (val filter = getItem(position).filter) {
is BooleanFilter -> R.layout.item_filter_boolean
is MultipleChoiceFilter -> {
if (filter.manyChoices) {
R.layout.item_filter_multiple_choice_large
} else {
R.layout.item_filter_multiple_choice
}
}
is SliderFilter -> R.layout.item_filter_slider
}
override fun bind(
holder: ViewHolder<FilterWithValue<FilterValue>>,
item: FilterWithValue<FilterValue>
) {
super.bind(holder, item)
when (item.value) {
is SliderFilterValue -> {
setupSlider(
holder.binding as ItemFilterSliderBinding,
item.filter as SliderFilter, item.value
)
}
is MultipleChoiceFilterValue -> {
val filter = item.filter as MultipleChoiceFilter
if (filter.manyChoices) {
setupMultipleChoiceMany(
holder.binding as ItemFilterMultipleChoiceLargeBinding,
filter, item.value
)
} else {
setupMultipleChoice(
holder.binding as ItemFilterMultipleChoiceBinding,
filter, item.value
)
}
}
}
}
private fun setupMultipleChoice(
binding: ItemFilterMultipleChoiceBinding,
filter: MultipleChoiceFilter,
value: MultipleChoiceFilterValue
) {
// TODO: this implementation seems to be buggy
val inflater = LayoutInflater.from(binding.root.context)
value.values.toList().forEach {
// delete values that cannot be selected anymore
if (it !in filter.choices.keys) value.values.remove(it)
}
fun updateButtons() {
value.all = value.values == filter.choices.keys
binding.btnAll.isEnabled = !value.all
binding.btnNone.isEnabled = value.values.isNotEmpty()
}
val chips = mutableMapOf<String, Chip>()
// reuse existing chips in layout
val reuseChips = binding.chipGroup.children.filter {
it.id != R.id.chipMore
}.toMutableList()
binding.chipGroup.children.forEach {
if (it.id != R.id.chipMore) binding.chipGroup.removeView(it)
}
filter.choices.entries.sortedByDescending {
it.key in value.values
}.sortedByDescending {
if (filter.commonChoices != null) it.key in filter.commonChoices else false
}.forEach { choice ->
var reused = false
val chip = if (reuseChips.size > 0) {
reused = true
reuseChips.removeAt(0) as Chip
} else {
inflater.inflate(
R.layout.item_filter_multiple_choice_chip,
binding.chipGroup,
false
) as Chip
}
chip.text = choice.value
chip.isChecked = choice.key in value.values || value.all
if (value.all && choice.key !in value.values) value.values.add(choice.key)
chip.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
value.values.add(choice.key)
} else {
value.values.remove(choice.key)
}
updateButtons()
}
if (filter.commonChoices != null && choice.key !in filter.commonChoices
&& !(chip.isChecked && !value.all) && !binding.showingAll
) {
chip.visibility = View.GONE
} else {
chip.visibility = View.VISIBLE
}
if (!reused) binding.chipGroup.addView(chip, binding.chipGroup.childCount - 1)
chips[choice.key] = chip
}
// delete surplus reusable chips
reuseChips.forEach {
binding.chipGroup.removeView(it)
}
binding.btnAll.setOnClickListener {
value.all = true
value.values.addAll(filter.choices.keys)
chips.values.forEach { it.isChecked = true }
updateButtons()
}
binding.btnNone.setOnClickListener {
value.all = true
value.values.addAll(filter.choices.keys)
chips.values.forEach { it.isChecked = false }
updateButtons()
}
binding.chipMore.setOnClickListener {
binding.showingAll = !binding.showingAll
chips.forEach { (key, chip) ->
if (filter.commonChoices != null && key !in filter.commonChoices
&& !(chip.isChecked && !value.all) && !binding.showingAll
) {
chip.visibility = View.GONE
} else {
chip.visibility = View.VISIBLE
}
}
}
updateButtons()
}
private fun setupMultipleChoiceMany(
binding: ItemFilterMultipleChoiceLargeBinding,
filter: MultipleChoiceFilter,
value: MultipleChoiceFilterValue
) {
if (value.all) {
value.values = filter.choices.keys.toMutableSet()
binding.notifyPropertyChanged(BR.item)
}
binding.btnEdit.setOnClickListener {
val dialog =
MultiSelectDialog.getInstance(
filter.name,
filter.choices,
value.values,
commonChoices = filter.commonChoices
)
dialog.okListener = { selected ->
value.values = selected.toMutableSet()
value.all = value.values == filter.choices.keys
binding.item = binding.item
}
dialog.show((binding.root.context as AppCompatActivity).supportFragmentManager, null)
}
}
private fun setupSlider(
binding: ItemFilterSliderBinding,
filter: SliderFilter,
value: SliderFilterValue
) {
binding.progress =
max(filter.inverseMapping(value.value) - filter.min, 0)
binding.mappedValue = filter.mapping(binding.progress + filter.min)
binding.addOnPropertyChangedCallback(object :
Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
when (propertyId) {
BR.progress -> {
val mapped = filter.mapping(binding.progress + filter.min)
value.value = mapped
binding.mappedValue = mapped
}
}
}
})
}
override fun getItemId(position: Int): Long {
val key = getItem(position).filter.key
var value = itemids[key]
if (value == null) {
maxId++
value = maxId
itemids[key] = maxId
}
return value
}
}
@@ -1,129 +0,0 @@
package net.vonforst.evmap.adapter
import android.annotation.SuppressLint
import android.content.Context
import android.view.*
import android.widget.ImageView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.ortiz.touchview.TouchImageView
import com.squareup.picasso.Callback
import com.squareup.picasso.Picasso
import net.vonforst.evmap.R
import net.vonforst.evmap.api.goingelectric.ChargerPhoto
class GalleryAdapter(
context: Context,
val itemClickListener: ItemClickListener? = null,
val detailView: Boolean = false,
val pageToLoad: Int? = null,
val loadedListener: (() -> Unit)? = null
) :
ListAdapter<ChargerPhoto, GalleryAdapter.ViewHolder>(ChargerPhotoDiffCallback()) {
class ViewHolder(val view: ImageView) : RecyclerView.ViewHolder(view)
interface ItemClickListener {
fun onItemClick(view: View, position: Int)
}
val apikey = context.getString(R.string.goingelectric_key)
var loaded = false
@SuppressLint("ClickableViewAccessibility")
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view: ImageView
if (detailView) {
view = inflater.inflate(R.layout.gallery_item_fullscreen, parent, false) as ImageView
view.setOnTouchListener { view, event ->
var result = true
//can scroll horizontally checks if there's still a part of the image
//that can be scrolled until you reach the edge
if (event.pointerCount >= 2 || view.canScrollHorizontally(1) && view.canScrollHorizontally(
-1
)
) {
//multi-touch event
result = when (event.action) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
// Disallow RecyclerView to intercept touch events.
parent.requestDisallowInterceptTouchEvent(true)
// Disable touch on view
false
}
MotionEvent.ACTION_UP -> {
// Allow RecyclerView to intercept touch events.
parent.requestDisallowInterceptTouchEvent(false)
true
}
else -> true
}
}
result
}
} else {
view = inflater.inflate(R.layout.gallery_item, parent, false) as ImageView
}
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
if (detailView) {
(holder.view as TouchImageView).resetZoom()
}
Picasso.get()
.load(
"https://api.goingelectric.de/chargepoints/photo/?key=${apikey}" +
"&id=${getItem(position).id}" +
if (detailView) {
"&size=1000"
} else {
"&height=${holder.view.height}"
}
)
.into(holder.view, object : Callback {
override fun onSuccess() {
if (!loaded && loadedListener != null && pageToLoad == position) {
holder.view.viewTreeObserver.addOnPreDrawListener(object :
ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
holder.view.viewTreeObserver.removeOnPreDrawListener(this)
loadedListener.invoke()
return true
}
})
loaded = true
}
}
override fun onError(e: Exception?) {
if (!loaded && loadedListener != null && pageToLoad == position) {
loadedListener.invoke()
loaded = true
}
}
})
holder.view.transitionName = galleryTransitionName(position)
if (itemClickListener != null) {
holder.view.setOnClickListener {
itemClickListener.onItemClick(holder.view, position)
}
}
}
}
fun galleryTransitionName(position: Int) = "gallery_$position"
class ChargerPhotoDiffCallback : DiffUtil.ItemCallback<ChargerPhoto>() {
override fun areItemsTheSame(oldItem: ChargerPhoto, newItem: ChargerPhoto): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: ChargerPhoto, newItem: ChargerPhoto): Boolean {
return oldItem.id == newItem.id
}
}
@@ -1,61 +0,0 @@
package net.vonforst.evmap.api
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Response
import org.json.JSONArray
import java.io.IOException
import kotlin.coroutines.resumeWithException
operator fun <T> JSONArray.iterator(): Iterator<T> =
(0 until length()).asSequence().map { get(it) as T }.iterator()
@ExperimentalCoroutinesApi
suspend fun Call.await(): Response {
return suspendCancellableCoroutine { continuation ->
enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
continuation.resume(response) {}
}
override fun onFailure(call: Call, e: IOException) {
if (continuation.isCancelled) return
continuation.resumeWithException(e)
}
})
continuation.invokeOnCancellation {
try {
cancel()
} catch (ex: Throwable) {
//Ignore cancel exception
}
}
}
}
const val earthRadiusKm: Double = 6372.8
/**
* Calculates the distance between two points on Earth in meters.
* Latitude and longitude should be given in degrees.
*/
fun distanceBetween(
startLatitude: Double, startLongitude: Double,
endLatitude: Double, endLongitude: Double
): Double {
// see https://rosettacode.org/wiki/Haversine_formula#Java
val dLat = Math.toRadians(endLatitude - startLatitude);
val dLon = Math.toRadians(endLongitude - startLongitude);
val originLat = Math.toRadians(startLatitude);
val destinationLat = Math.toRadians(endLatitude);
val a = Math.pow(Math.sin(dLat / 2), 2.toDouble()) + Math.pow(
Math.sin(dLon / 2),
2.toDouble()
) * Math.cos(originLat) * Math.cos(destinationLat);
val c = 2 * Math.asin(Math.sqrt(a));
return earthRadiusKm * c * 1000;
}
@@ -1,155 +0,0 @@
package net.vonforst.evmap.api.availability
import com.facebook.stetho.okhttp3.StethoInterceptor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import net.vonforst.evmap.api.await
import net.vonforst.evmap.api.goingelectric.ChargeLocation
import net.vonforst.evmap.api.goingelectric.Chargepoint
import net.vonforst.evmap.viewmodel.Resource
import okhttp3.OkHttpClient
import okhttp3.Request
import retrofit2.HttpException
import java.io.IOException
import java.util.concurrent.TimeUnit
interface AvailabilityDetector {
suspend fun getAvailability(location: ChargeLocation): ChargeLocationStatus
}
abstract class BaseAvailabilityDetector(private val client: OkHttpClient) : AvailabilityDetector {
protected val radius = 150 // max radius in meters
protected suspend fun httpGet(url: String): String {
val request = Request.Builder().url(url).build()
val response = client.newCall(request).await()
if (!response.isSuccessful) throw IOException(response.message())
val str = response.body()!!.string()
return str
}
protected fun getCorrespondingChargepoint(
cps: Iterable<Chargepoint>, type: String, power: Double
): Chargepoint? {
var filter = cps.filter {
it.type == type
}
if (filter.size > 1) {
filter = filter.filter {
if (power > 0) {
it.power == power
} else true
}
// TODO: handle not matching powers
/*if (filter.isEmpty()) {
filter = listOfNotNull(cps.minBy {
abs(it.power - power)
})
}*/
}
return filter.getOrNull(0)
}
companion object {
internal fun matchChargepoints(
connectors: Map<Long, Pair<Double, String>>,
chargepoints: List<Chargepoint>
): Map<Chargepoint, Set<Long>> {
// iterate over each connector type
val types = connectors.map { it.value.second }.distinct().toSet()
val geTypes = chargepoints.map { it.type }.distinct().toSet()
if (types != geTypes) throw AvailabilityDetectorException("chargepoints do not match")
return types.flatMap { type ->
// find connectors of this type
val connsOfType = connectors.filter { it.value.second == type }
// find powers this connector is available as
val powers = connsOfType.map { it.value.first }.distinct().sorted()
// find corresponding powers in GE data
val gePowers =
chargepoints.filter { it.type == type }.map { it.power }.distinct().sorted()
// if the distinct number of powers is the same, try to match.
if (powers.size == gePowers.size) {
gePowers.zip(powers).map { (gePower, power) ->
val chargepoint =
chargepoints.find { it.type == type && it.power == gePower }!!
val ids = connsOfType.filter { it.value.first == power }.keys
if (chargepoint.count != ids.size) {
throw AvailabilityDetectorException("chargepoints do not match")
}
chargepoint to ids
}
} else if (powers.size == 1 && gePowers.size == 2
&& chargepoints.sumBy { it.count } == connsOfType.size
) {
// special case: dual charger(s) with load balancing
// GoingElectric shows 2 different powers, NewMotion just one
val allIds = connsOfType.keys.toList()
var i = 0
gePowers.map { gePower ->
val chargepoint =
chargepoints.find { it.type == type && it.power == gePower }!!
val ids = allIds.subList(i, i + chargepoint.count).toSet()
i += chargepoint.count
chargepoint to ids
}
// TODO: this will not necessarily first fill up the higher-power chargepoint
} else {
throw AvailabilityDetectorException("chargepoints do not match")
}
}.toMap()
}
}
}
data class ChargeLocationStatus(
val status: Map<Chargepoint, List<ChargepointStatus>>,
val source: String
)
enum class ChargepointStatus {
AVAILABLE, UNKNOWN, CHARGING, OCCUPIED, FAULTED
}
class AvailabilityDetectorException(message: String) : Exception(message)
private val okhttp = OkHttpClient.Builder()
.addNetworkInterceptor(StethoInterceptor())
.readTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.build()
val availabilityDetectors = listOf(
NewMotionAvailabilityDetector(okhttp)
/*ChargecloudAvailabilityDetector(
okhttp,
"606a0da0dfdd338ee4134605653d4fd8"
), // Maingau
ChargecloudAvailabilityDetector(
okhttp,
"6336fe713f2eb7fa04b97ff6651b76f8"
) // SW Kiel*/
)
suspend fun getAvailability(charger: ChargeLocation): Resource<ChargeLocationStatus> {
var value: Resource<ChargeLocationStatus>? = null
withContext(Dispatchers.IO) {
for (ad in availabilityDetectors) {
try {
value = Resource.success(ad.getAvailability(charger))
break
} catch (e: IOException) {
value = Resource.error(e.message, null)
e.printStackTrace()
} catch (e: HttpException) {
value = Resource.error(e.message, null)
e.printStackTrace()
} catch (e: AvailabilityDetectorException) {
value = Resource.error(e.message, null)
e.printStackTrace()
}
}
}
return value ?: Resource.error(null, null)
}
@@ -1,94 +0,0 @@
package net.vonforst.evmap.api.availability
import kotlinx.coroutines.ExperimentalCoroutinesApi
import net.vonforst.evmap.api.goingelectric.ChargeLocation
import net.vonforst.evmap.api.goingelectric.Chargepoint
import net.vonforst.evmap.api.iterator
import okhttp3.OkHttpClient
import org.json.JSONObject
import java.io.IOException
class ChargecloudAvailabilityDetector(
client: OkHttpClient,
private val operatorId: String
) : BaseAvailabilityDetector(client) {
@ExperimentalCoroutinesApi
override suspend fun getAvailability(location: ChargeLocation): ChargeLocationStatus {
val url =
"https://app.chargecloud.de/emobility:ocpi/$operatorId/app/2.0/locations?latitude=${location.coordinates.lat}&longitude=${location.coordinates.lng}&radius=$radius&offset=0&limit=10"
val json = JSONObject(httpGet(url))
val statusMessage = json.getString("status_message")
if (statusMessage != "Success") throw IOException(statusMessage)
val data = json.getJSONArray("data")
if (data.length() > 1) throw AvailabilityDetectorException(
"found multiple candidates."
)
if (data.length() == 0) throw AvailabilityDetectorException(
"no candidates found."
)
val evses = data.getJSONObject(0).getJSONArray("evses")
val chargepointStatus = mutableMapOf<Chargepoint, List<ChargepointStatus>>()
evses.iterator<JSONObject>().forEach { evse ->
evse.getJSONArray("connectors").iterator<JSONObject>().forEach connector@{ connector ->
val type = getType(connector.getString("standard"))
val power = connector.getDouble("max_power")
val status = ChargepointStatus.valueOf(connector.getString("status"))
var chargepoint = getCorrespondingChargepoint(chargepointStatus.keys, type, power)
val statusList: List<ChargepointStatus>
if (chargepoint == null) {
// find corresponding chargepoint from goingelectric to get correct power
val geChargepoint =
getCorrespondingChargepoint(location.chargepointsMerged, type, power)
?: throw AvailabilityDetectorException(
"Chargepoints from chargecloud API and goingelectric do not match."
)
chargepoint = Chargepoint(
type,
geChargepoint.power,
1
)
statusList = listOf(status)
} else {
val previousStatus = chargepointStatus[chargepoint]!!
statusList = previousStatus + listOf(status)
chargepointStatus.remove(chargepoint)
chargepoint =
Chargepoint(
chargepoint.type,
chargepoint.power,
chargepoint.count + 1
)
}
chargepointStatus[chargepoint] = statusList
}
}
if (chargepointStatus.keys == location.chargepointsMerged.toSet()) {
return ChargeLocationStatus(
chargepointStatus,
"chargecloud.de"
)
} else {
throw AvailabilityDetectorException(
"Chargepoints from chargecloud API and goingelectric do not match."
)
}
}
private fun getType(string: String): String {
return when (string) {
"IEC_62196_T2" -> Chargepoint.TYPE_2
"DOMESTIC_F" -> Chargepoint.SCHUKO
"IEC_62196_T2_COMBO" -> Chargepoint.CCS
"CHADEMO" -> Chargepoint.CHADEMO
else -> throw IllegalArgumentException("unrecognized type $string")
}
}
}
@@ -1,172 +0,0 @@
package net.vonforst.evmap.api.availability
import com.squareup.moshi.JsonClass
import net.vonforst.evmap.api.distanceBetween
import net.vonforst.evmap.api.goingelectric.ChargeLocation
import net.vonforst.evmap.api.goingelectric.Chargepoint
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
import retrofit2.http.Path
private const val coordRange = 0.1 // range of latitude and longitude for loading the map
private const val maxDistance = 15 // max distance between reported positions in meters
interface NewMotionApi {
@GET("markers/{lngMin}/{lngMax}/{latMin}/{latMax}")
suspend fun getMarkers(
@Path("lngMin") lngMin: Double,
@Path("lngMax") lngMax: Double,
@Path("latMin") latMin: Double,
@Path("latMax") latMax: Double
): List<NMMarker>
@GET("locations/{id}")
suspend fun getLocation(@Path("id") id: Long): NMLocation
@JsonClass(generateAdapter = true)
data class NMMarker(val coordinates: NMCoordinates, val locationUid: Long)
@JsonClass(generateAdapter = true)
data class NMCoordinates(val latitude: Double, val longitude: Double)
@JsonClass(generateAdapter = true)
data class NMLocation(
val uid: Long,
val coordinates: NMCoordinates,
val operatorName: String,
val evses: List<NMEvse>
)
@JsonClass(generateAdapter = true)
data class NMEvse(val evseId: String?, val status: String, val connectors: List<NMConnector>)
@JsonClass(generateAdapter = true)
data class NMConnector(
val uid: Long,
val connectorType: String,
val electricalProperties: NMElectricalProperties
)
@JsonClass(generateAdapter = true)
data class NMElectricalProperties(val powerType: String, val voltage: Int, val amperage: Int) {
fun getPower(): Double {
val phases = when (powerType) {
"AC1Phase" -> 1
"AC3Phase" -> 3
else -> 1
}
val volt = when (voltage) {
277 -> 230
else -> voltage
}
val power = volt * amperage * phases
return when (power) {
3680 -> 3.7
11040 -> 11.0
22080 -> 22.0
43470 -> 43.0
else -> power / 1000.0
}
}
}
companion object {
fun create(client: OkHttpClient, baseUrl: String? = null): NewMotionApi {
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl ?: "https://my.newmotion.com/api/map/v2/")
.addConverterFactory(MoshiConverterFactory.create())
.client(client)
.build()
return retrofit.create(NewMotionApi::class.java)
}
}
}
class NewMotionAvailabilityDetector(client: OkHttpClient, baseUrl: String? = null) :
BaseAvailabilityDetector(client) {
val api = NewMotionApi.create(client, baseUrl)
override suspend fun getAvailability(location: ChargeLocation): ChargeLocationStatus {
val lat = location.coordinates.lat
val lng = location.coordinates.lng
// find nearest station to this position
var markers =
api.getMarkers(lng - coordRange, lng + coordRange, lat - coordRange, lat + coordRange)
val nearest = markers.minBy { marker ->
distanceBetween(marker.coordinates.latitude, marker.coordinates.longitude, lat, lng)
} ?: throw AvailabilityDetectorException("no candidates found.")
if (distanceBetween(
nearest.coordinates.latitude,
nearest.coordinates.longitude,
lat,
lng
) > radius
) {
throw AvailabilityDetectorException("no candidates found")
}
// combine related stations
markers = markers.filter { marker ->
distanceBetween(
marker.coordinates.latitude,
marker.coordinates.longitude,
nearest.coordinates.latitude,
nearest.coordinates.longitude
) < maxDistance
}
// load details
var details = markers.map {
api.getLocation(it.locationUid)
}
// only include stations from same operator
details = details.filter {
it.operatorName == details[0].operatorName
}
val connectorStatus = details.flatMap { it.evses }.flatMap { evse ->
evse.connectors.map { connector ->
connector to evse.status
}
}
val nmConnectors = mutableMapOf<Long, Pair<Double, String>>()
val nmStatus = mutableMapOf<Long, ChargepointStatus>()
connectorStatus.forEach { (connector, statusStr) ->
val id = connector.uid
val power = connector.electricalProperties.getPower()
val type = when (connector.connectorType) {
"Type3" -> Chargepoint.TYPE_3
"Type2" -> Chargepoint.TYPE_2
"Type1" -> Chargepoint.TYPE_1
"Domestic" -> Chargepoint.SCHUKO
"Type2Combo" -> Chargepoint.CCS
"TepcoCHAdeMO" -> Chargepoint.CHADEMO
"Unspecified" -> "unspecified"
else -> throw IllegalArgumentException("unrecognized type ${connector.connectorType}")
}
val status = when (statusStr) {
"Unavailable" -> ChargepointStatus.FAULTED
"Available" -> ChargepointStatus.AVAILABLE
"Occupied" -> ChargepointStatus.CHARGING
"Unspecified" -> ChargepointStatus.UNKNOWN
else -> ChargepointStatus.UNKNOWN
}
nmConnectors.put(id, power to type)
nmStatus.put(id, status)
}
val match = matchChargepoints(nmConnectors, location.chargepointsMerged)
val chargepointStatus = match.mapValues { entry ->
entry.value.map { nmStatus[it]!! }
}
return ChargeLocationStatus(
chargepointStatus,
"NewMotion"
)
}
}
@@ -1,160 +0,0 @@
package net.vonforst.evmap.api.goingelectric
import com.squareup.moshi.*
import java.lang.reflect.Type
import java.time.Instant
import java.time.LocalTime
internal class ChargepointListItemJsonAdapterFactory : JsonAdapter.Factory {
override fun create(
type: Type,
annotations: MutableSet<out Annotation>,
moshi: Moshi
): JsonAdapter<*>? {
if (Types.getRawType(type) == ChargepointListItem::class.java) {
return ChargepointListItemJsonAdapter(
moshi
)
} else {
return null
}
}
}
internal class ChargepointListItemJsonAdapter(val moshi: Moshi) :
JsonAdapter<ChargepointListItem>() {
private val clusterAdapter =
moshi.adapter<ChargeLocationCluster>(
ChargeLocationCluster::class.java
)
private val locationAdapter = moshi.adapter<ChargeLocation>(
ChargeLocation::class.java
)
@FromJson
override fun fromJson(reader: JsonReader): ChargepointListItem {
var clustered = false
reader.peekJson().use { peeked ->
peeked.beginObject()
while (peeked.hasNext()) {
if (peeked.selectName(CLUSTERED) == 0) {
clustered = peeked.nextBoolean()
break
}
peeked.skipName()
peeked.skipValue()
}
}
return if (clustered) {
clusterAdapter.fromJson(reader)!!
} else {
locationAdapter.fromJson(reader)!!
}
}
companion object {
val CLUSTERED: JsonReader.Options = JsonReader.Options.of("clustered")
}
override fun toJson(writer: JsonWriter, value: ChargepointListItem?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
internal class JsonObjectOrFalseAdapter<T> private constructor(
private val objectDelegate: JsonAdapter<T>,
private val clazz: Class<*>
) : JsonAdapter<T>() {
class Factory() : JsonAdapter.Factory {
override fun create(
type: Type,
annotations: Set<Annotation>?,
moshi: Moshi
): JsonAdapter<Any>? {
val clazz = Types.getRawType(type)
return when (hasJsonObjectOrFalseAnnotation(
annotations
)) {
false -> null
true -> JsonObjectOrFalseAdapter(
moshi.adapter(type), clazz
)
}
}
}
@Suppress("UNCHECKED_CAST")
override fun fromJson(reader: JsonReader): T? = when (reader.peek()) {
JsonReader.Token.BOOLEAN -> when (reader.nextBoolean()) {
false -> null // Response was false
else -> {
if (this.clazz == FaultReport::class.java) {
FaultReport(null, null) as T
} else {
throw IllegalStateException("Non-false boolean for @JsonObjectOrFalse field")
}
}
}
JsonReader.Token.BEGIN_OBJECT -> objectDelegate.fromJson(reader)
JsonReader.Token.BEGIN_ARRAY -> objectDelegate.fromJson(reader)
JsonReader.Token.STRING -> objectDelegate.fromJson(reader)
JsonReader.Token.NUMBER -> objectDelegate.fromJson(reader)
else ->
throw IllegalStateException("Non-object-non-boolean value for @JsonObjectOrFalse field")
}
override fun toJson(writer: JsonWriter, value: T?) = objectDelegate.toJson(writer, value)
}
private fun hasJsonObjectOrFalseAnnotation(annotations: Set<Annotation>?) =
annotations?.firstOrNull { it.annotationClass == JsonObjectOrFalse::class } != null
@JsonQualifier
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
annotation class JsonObjectOrFalse {
}
internal class HoursAdapter {
private val regex = Regex("from (.*) till (.*)")
@FromJson
fun fromJson(str: String): Hours? {
if (str == "closed") {
return Hours(null, null)
} else {
val match = regex.find(str)
?: throw IllegalArgumentException("$str does not match hours format")
return Hours(
LocalTime.parse(match.groupValues[1]),
LocalTime.parse(match.groupValues[2])
)
}
}
@ToJson
fun toJson(value: Hours): String {
if (value.start == null || value.end == null) {
return "closed"
} else {
return "from ${value.start} till ${value.end}"
}
}
}
internal class InstantAdapter {
@FromJson
fun fromJson(value: Long?): Instant? = value?.let {
Instant.ofEpochSecond(it)
}
@ToJson
fun toJson(value: Instant?): Long? = value?.epochSecond
}
@@ -1,87 +0,0 @@
package net.vonforst.evmap.api.goingelectric
import android.content.Context
import com.facebook.stetho.okhttp3.StethoInterceptor
import com.squareup.moshi.Moshi
import net.vonforst.evmap.BuildConfig
import okhttp3.Cache
import okhttp3.OkHttpClient
import retrofit2.Call
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
import retrofit2.http.Query
interface GoingElectricApi {
@GET("chargepoints/")
suspend fun getChargepoints(
@Query("sw_lat") swlat: Double, @Query("sw_lng") sw_lng: Double,
@Query("ne_lat") ne_lat: Double, @Query("ne_lng") ne_lng: Double,
@Query("zoom") zoom: Float,
@Query("clustering") clustering: Boolean = false,
@Query("cluster_distance") clusterDistance: Int? = null,
@Query("freecharging") freecharging: Boolean = false,
@Query("freeparking") freeparking: Boolean = false,
@Query("min_power") minPower: Int = 0,
@Query("plugs") plugs: String? = null,
@Query("chargecards") chargecards: String? = null,
@Query("networks") networks: String? = null,
@Query("startkey") startkey: Int? = null,
@Query("open_twentyfourseven") open247: Boolean = false,
@Query("barrierfree") barrierfree: Boolean = false,
@Query("exclude_faults") excludeFaults: Boolean = false
): Response<ChargepointList>
@GET("chargepoints/")
fun getChargepointDetail(@Query("ge_id") id: Long): Call<ChargepointList>
@GET("chargepoints/pluglist/")
suspend fun getPlugs(): Response<StringList>
@GET("chargepoints/networklist/")
suspend fun getNetworks(): Response<StringList>
@GET("chargepoints/chargecardlist/")
suspend fun getChargeCards(): Response<ChargeCardList>
companion object {
private val cacheSize = 10L * 1024 * 1024; // 10MB
fun create(
apikey: String,
baseurl: String = "https://api.goingelectric.de",
context: Context? = null
): GoingElectricApi {
val client = OkHttpClient.Builder().apply {
addInterceptor { chain ->
// add API key to every request
var original = chain.request()
val url = original.url().newBuilder().addQueryParameter("key", apikey).build()
original = original.newBuilder().url(url).build()
chain.proceed(original)
}
if (BuildConfig.DEBUG) {
addNetworkInterceptor(StethoInterceptor())
}
if (context != null) {
cache(Cache(context.getCacheDir(), cacheSize))
}
}.build()
val moshi = Moshi.Builder()
.add(ChargepointListItemJsonAdapterFactory())
.add(JsonObjectOrFalseAdapter.Factory())
.add(HoursAdapter())
.add(InstantAdapter())
.build()
val retrofit = Retrofit.Builder()
.baseUrl(baseurl)
.addConverterFactory(MoshiConverterFactory.create(moshi))
.client(client)
.build()
return retrofit.create(GoingElectricApi::class.java)
}
}
}
@@ -1,297 +0,0 @@
package net.vonforst.evmap.api.goingelectric
import android.content.Context
import android.os.Parcelable
import androidx.core.text.HtmlCompat
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import kotlinx.android.parcel.Parcelize
import net.vonforst.evmap.R
import net.vonforst.evmap.adapter.Equatable
import java.time.DayOfWeek
import java.time.Instant
import java.time.LocalDate
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.util.*
import kotlin.math.abs
import kotlin.math.floor
@JsonClass(generateAdapter = true)
data class ChargepointList(
val status: String,
val chargelocations: List<ChargepointListItem>,
@JsonObjectOrFalse val startkey: Int?
)
@JsonClass(generateAdapter = true)
data class StringList(
val status: String,
val result: List<String>
)
@JsonClass(generateAdapter = true)
data class ChargeCardList(
val status: String,
val result: List<ChargeCard>
)
sealed class ChargepointListItem
@JsonClass(generateAdapter = true)
@Entity
data class ChargeLocation(
@Json(name = "ge_id") @PrimaryKey val id: Long,
val name: String,
@Embedded val coordinates: Coordinate,
@Embedded val address: Address,
val chargepoints: List<Chargepoint>,
@JsonObjectOrFalse val network: String?,
val url: String,
@Embedded(prefix = "fault_report_") @JsonObjectOrFalse @Json(name = "fault_report") val faultReport: FaultReport?,
val verified: Boolean,
// only shown in details:
@JsonObjectOrFalse val operator: String?,
@JsonObjectOrFalse @Json(name = "general_information") val generalInformation: String?,
@JsonObjectOrFalse @Json(name = "ladeweile") val amenities: String?,
@JsonObjectOrFalse @Json(name = "location_description") val locationDescription: String?,
val photos: List<ChargerPhoto>?,
@JsonObjectOrFalse val chargecards: List<ChargeCardId>?,
@Embedded val openinghours: OpeningHours?,
@Embedded val cost: Cost?
) : ChargepointListItem(), Equatable {
/**
* maximum power available from this charger.
*/
val maxPower: Double
get() {
return maxPower()
}
/**
* Gets the maximum power available from certain connectors of this charger.
*/
fun maxPower(connectors: Set<String>? = null): Double {
return chargepoints.filter { connectors?.contains(it.type) ?: true }
.map { it.power }.max() ?: 0.0
}
/**
* Merges chargepoints if they have the same plug and power
*
* This occurs e.g. for Type2 sockets and plugs, which are distinct on the GE website, but not
* separable in the API
*/
val chargepointsMerged: List<Chargepoint>
get() {
val variants = chargepoints.distinctBy { it.power to it.type }
return variants.map { variant ->
val count = chargepoints
.filter { it.type == variant.type && it.power == variant.power }
.sumBy { it.count }
Chargepoint(variant.type, variant.power, count)
}
}
fun formatChargepoints(): String {
return chargepointsMerged.map {
"${it.count} × ${it.type} ${it.formatPower()}"
}.joinToString(" · ")
}
}
@JsonClass(generateAdapter = true)
data class Cost(
val freecharging: Boolean,
val freeparking: Boolean,
@JsonObjectOrFalse @Json(name = "description_short") val descriptionShort: String?,
@JsonObjectOrFalse @Json(name = "description_long") val descriptionLong: String?
) {
fun getStatusText(ctx: Context): CharSequence {
return HtmlCompat.fromHtml(
ctx.getString(
R.string.cost_detail,
if (freecharging) ctx.getString(R.string.free) else ctx.getString(R.string.paid),
if (freeparking) ctx.getString(R.string.free) else ctx.getString(R.string.paid)
), 0
)
}
}
@JsonClass(generateAdapter = true)
data class OpeningHours(
@Json(name = "24/7") val twentyfourSeven: Boolean,
@JsonObjectOrFalse val description: String?,
@Embedded val days: OpeningHoursDays?
) {
val isEmpty: Boolean
get() = description == "Leider noch keine Informationen zu Öffnungszeiten vorhanden."
&& days == null && !twentyfourSeven
fun getStatusText(ctx: Context): CharSequence {
if (twentyfourSeven) {
return HtmlCompat.fromHtml(ctx.getString(R.string.open_247), 0)
} else if (days != null) {
val hours = days.getHoursForDate(LocalDate.now())
if (hours.start == null || hours.end == null) {
return HtmlCompat.fromHtml(ctx.getString(R.string.closed), 0)
}
val now = LocalTime.now()
if (hours.start.isBefore(now) && hours.end.isAfter(now)) {
return HtmlCompat.fromHtml(
ctx.getString(
R.string.open_closesat,
hours.end.toString()
), 0
)
} else if (hours.end.isBefore(now)) {
return HtmlCompat.fromHtml(ctx.getString(R.string.closed), 0)
} else {
return HtmlCompat.fromHtml(
ctx.getString(
R.string.closed_opensat,
hours.start.toString()
), 0
)
}
} else {
return ""
}
}
}
@JsonClass(generateAdapter = true)
data class OpeningHoursDays(
@Embedded(prefix = "mo") val monday: Hours,
@Embedded(prefix = "tu") val tuesday: Hours,
@Embedded(prefix = "we") val wednesday: Hours,
@Embedded(prefix = "th") val thursday: Hours,
@Embedded(prefix = "fr") val friday: Hours,
@Embedded(prefix = "sa") val saturday: Hours,
@Embedded(prefix = "su") val sunday: Hours,
@Embedded(prefix = "ho") val holiday: Hours
) {
fun getHoursForDate(date: LocalDate): Hours {
// TODO: check for holidays
return getHoursForDayOfWeek(date.dayOfWeek)
}
fun getHoursForDayOfWeek(dayOfWeek: DayOfWeek?): Hours {
@Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA")
return when (dayOfWeek) {
DayOfWeek.MONDAY -> monday
DayOfWeek.TUESDAY -> tuesday
DayOfWeek.WEDNESDAY -> wednesday
DayOfWeek.THURSDAY -> thursday
DayOfWeek.FRIDAY -> friday
DayOfWeek.SATURDAY -> saturday
DayOfWeek.SUNDAY -> sunday
null -> holiday
}
}
}
data class Hours(
val start: LocalTime?,
val end: LocalTime?
) {
override fun toString(): String {
if (start != null && end != null) {
val fmt = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
return "${start.format(fmt)} - ${end.format(fmt)}"
} else {
return "closed"
}
}
}
@JsonClass(generateAdapter = true)
@Parcelize
data class ChargerPhoto(val id: String) : Parcelable
@JsonClass(generateAdapter = true)
data class ChargeLocationCluster(
val clusterCount: Int,
val coordinates: Coordinate
) : ChargepointListItem()
@JsonClass(generateAdapter = true)
data class Coordinate(val lat: Double, val lng: Double) {
fun formatDMS(): String {
return "${dms(lat, false)}, ${dms(lng, true)}"
}
private fun dms(value: Double, lon: Boolean): String {
val hemisphere = if (lon) {
if (value >= 0) "E" else "W"
} else {
if (value >= 0) "N" else "S"
}
val d = abs(value)
val degrees = floor(d).toInt()
val minutes = floor((d - degrees) * 60).toInt()
val seconds = ((d - degrees) * 60 - minutes) * 60
return "%d°%02d'%02.1f\"%s".format(Locale.ENGLISH, degrees, minutes, seconds, hemisphere)
}
fun formatDecimal(): String {
return "%.6f, %.6f".format(Locale.ENGLISH, lat, lng)
}
}
@JsonClass(generateAdapter = true)
data class Address(
@JsonObjectOrFalse val city: String?,
@JsonObjectOrFalse val country: String?,
@JsonObjectOrFalse val postcode: String?,
@JsonObjectOrFalse val street: String?
) {
override fun toString(): String {
return "${street ?: ""}, ${postcode ?: ""} ${city ?: ""}"
}
}
@JsonClass(generateAdapter = true)
data class Chargepoint(val type: String, val power: Double, val count: Int) : Equatable {
fun formatPower(): String {
val powerFmt = if (power - power.toInt() == 0.0) {
"%.0f".format(power)
} else {
"%.1f".format(power)
}
return "$powerFmt kW"
}
companion object {
const val TYPE_1 = "Typ1"
const val TYPE_2 = "Typ2"
const val TYPE_3 = "Typ3"
const val CCS = "CCS"
const val SCHUKO = "Schuko"
const val CHADEMO = "CHAdeMO"
const val SUPERCHARGER = "Tesla Supercharger"
const val CEE_BLAU = "CEE Blau"
const val CEE_ROT = "CEE Rot"
}
}
@JsonClass(generateAdapter = true)
data class FaultReport(val created: Instant?, val description: String?)
@Entity
@JsonClass(generateAdapter = true)
data class ChargeCard(
@Json(name = "card_id") @PrimaryKey val id: Long,
val name: String,
val url: String
)
@JsonClass(generateAdapter = true)
data class ChargeCardId(
val id: Long
)
@@ -1,66 +0,0 @@
package net.vonforst.evmap.fragment
import android.os.Bundle
import android.view.View
import androidx.appcompat.widget.Toolbar
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.mikepenz.aboutlibraries.LibsBuilder
import net.vonforst.evmap.BuildConfig
import net.vonforst.evmap.MapsActivity
import net.vonforst.evmap.R
class AboutFragment : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val toolbar = view.findViewById(R.id.toolbar) as Toolbar
val navController = findNavController()
toolbar.setupWithNavController(
navController,
(requireActivity() as MapsActivity).appBarConfiguration
)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.about, rootKey)
findPreference<Preference>("version")?.summary = BuildConfig.VERSION_NAME
}
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
return when (preference?.key) {
"github_link" -> {
(activity as? MapsActivity)?.openUrl(getString(R.string.github_link))
true
}
"privacy" -> {
(activity as? MapsActivity)?.openUrl(getString(R.string.privacy_link))
true
}
"faq" -> {
(activity as? MapsActivity)?.openUrl(getString(R.string.faq_link))
true
}
"oss_licenses" -> {
LibsBuilder()
.withLicenseShown(true)
.withAboutVersionShown(false)
.withAboutIconShown(false)
.withActivityTitle(getString(R.string.oss_licenses))
.withExcludedLibraries()
.start(requireActivity())
true
}
"donate" -> {
findNavController().navigate(R.id.action_about_to_donateFragment)
true
}
else -> super.onPreferenceTreeClick(preference)
}
}
}
@@ -1,104 +0,0 @@
package net.vonforst.evmap.fragment
import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.car2go.maps.model.LatLng
import com.mapzen.android.lost.api.LocationServices
import com.mapzen.android.lost.api.LostApiClient
import net.vonforst.evmap.MapsActivity
import net.vonforst.evmap.R
import net.vonforst.evmap.adapter.FavoritesAdapter
import net.vonforst.evmap.databinding.FragmentFavoritesBinding
import net.vonforst.evmap.viewmodel.FavoritesViewModel
import net.vonforst.evmap.viewmodel.viewModelFactory
class FavoritesFragment : Fragment(), LostApiClient.ConnectionCallbacks {
private lateinit var binding: FragmentFavoritesBinding
private lateinit var locationClient: LostApiClient
private val vm: FavoritesViewModel by viewModels(factoryProducer = {
viewModelFactory {
FavoritesViewModel(
requireActivity().application,
getString(R.string.goingelectric_key)
)
}
})
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_favorites, container, false
)
binding.lifecycleOwner = this
binding.vm = vm
locationClient = LostApiClient.Builder(requireContext())
.addConnectionCallbacks(this).build()
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val toolbar = view.findViewById(R.id.toolbar) as Toolbar
val navController = findNavController()
toolbar.setupWithNavController(
navController,
(requireActivity() as MapsActivity).appBarConfiguration
)
val favAdapter = FavoritesAdapter(vm).apply {
onClickListener = {
navController.navigate(R.id.action_favs_to_map, MapFragment.showCharger(it.charger))
}
}
binding.favsList.apply {
adapter = favAdapter
layoutManager =
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
addItemDecoration(
DividerItemDecoration(
context, LinearLayoutManager.VERTICAL
)
)
}
locationClient.connect()
}
override fun onConnected() {
if (ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
val location = LocationServices.FusedLocationApi.getLastLocation(locationClient)
if (location != null) {
vm.location.value = LatLng(location.latitude, location.longitude)
}
}
}
override fun onConnectionSuspended() {
}
}
@@ -1,91 +0,0 @@
package net.vonforst.evmap.fragment
import android.os.Bundle
import android.view.*
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.launch
import net.vonforst.evmap.MapsActivity
import net.vonforst.evmap.R
import net.vonforst.evmap.adapter.FiltersAdapter
import net.vonforst.evmap.databinding.FragmentFilterBinding
import net.vonforst.evmap.viewmodel.FilterViewModel
import net.vonforst.evmap.viewmodel.viewModelFactory
class FilterFragment : Fragment() {
private lateinit var binding: FragmentFilterBinding
private val vm: FilterViewModel by viewModels(factoryProducer = {
viewModelFactory {
FilterViewModel(
requireActivity().application,
getString(R.string.goingelectric_key)
)
}
})
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_filter, container, false)
binding.lifecycleOwner = this
binding.vm = vm
setHasOptionsMenu(true)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val toolbar = view.findViewById(R.id.toolbar) as Toolbar
(requireActivity() as AppCompatActivity).setSupportActionBar(toolbar)
val navController = findNavController()
toolbar.setupWithNavController(
navController,
(requireActivity() as MapsActivity).appBarConfiguration
)
binding.filtersList.apply {
adapter = FiltersAdapter()
layoutManager =
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
addItemDecoration(
DividerItemDecoration(
context, LinearLayoutManager.VERTICAL
)
)
}
toolbar.setNavigationOnClickListener {
findNavController().popBackStack()
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.filter, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.menu_apply -> {
lifecycleScope.launch {
vm.saveFilterValues()
findNavController().popBackStack()
}
true
}
else -> super.onOptionsItemSelected(item)
}
}
}
@@ -1,130 +0,0 @@
package net.vonforst.evmap.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback
import androidx.core.app.SharedElementCallback
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.transition.TransitionInflater
import androidx.viewpager2.widget.ViewPager2
import com.ortiz.touchview.TouchImageView
import net.vonforst.evmap.R
import net.vonforst.evmap.adapter.GalleryAdapter
import net.vonforst.evmap.api.goingelectric.ChargerPhoto
import net.vonforst.evmap.databinding.FragmentGalleryBinding
import net.vonforst.evmap.viewmodel.GalleryViewModel
class GalleryFragment : Fragment() {
companion object {
private const val EXTRA_POSITION = "position"
private const val EXTRA_PHOTOS = "photos"
private const val SAVED_CURRENT_PAGE_POSITION = "current_page_position"
fun buildArgs(photos: List<ChargerPhoto>, position: Int): Bundle {
return Bundle().apply {
putParcelableArrayList(EXTRA_PHOTOS, ArrayList(photos))
putInt(EXTRA_POSITION, position)
}
}
}
private lateinit var binding: FragmentGalleryBinding
private var isReturning: Boolean = false
private var startingPosition: Int = 0
private var currentPosition: Int = 0
private lateinit var galleryAdapter: GalleryAdapter
private var currentPage: TouchImageView? = null
private val galleryVm: GalleryViewModel by activityViewModels()
private val backPressedCallback = object :
OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
val image = currentPage
if (image != null && image.currentZoom !in 0.95f..1.05f) {
image.setZoomAnimated(1f, 0.5f, 0.5f)
} else {
isReturning = true
galleryVm.galleryPosition.value = currentPosition
findNavController().popBackStack()
}
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_gallery, container, false
)
binding.lifecycleOwner = this
val args = requireArguments()
startingPosition = args.getInt(EXTRA_POSITION, 0)
currentPosition =
savedInstanceState?.getInt(SAVED_CURRENT_PAGE_POSITION) ?: startingPosition
galleryAdapter =
GalleryAdapter(requireContext(), detailView = true, pageToLoad = currentPosition) {
startPostponedEnterTransition()
}
binding.gallery.setPageTransformer { page, position ->
val v = page as TouchImageView
currentPage = v
}
binding.gallery.adapter = galleryAdapter
binding.photos = args.getParcelableArrayList(EXTRA_PHOTOS)
binding.gallery.post {
binding.gallery.setCurrentItem(currentPosition, false)
binding.gallery.registerOnPageChangeCallback(object :
ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
currentPosition = position
}
})
}
sharedElementEnterTransition = TransitionInflater.from(context)
.inflateTransition(R.transition.image_shared_element_transition)
sharedElementReturnTransition = TransitionInflater.from(context)
.inflateTransition(R.transition.image_shared_element_transition)
setEnterSharedElementCallback(enterElementCallback)
if (savedInstanceState == null) {
postponeEnterTransition();
}
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
backPressedCallback
)
return binding.root
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt(SAVED_CURRENT_PAGE_POSITION, currentPosition)
}
private val enterElementCallback: SharedElementCallback = object : SharedElementCallback() {
override fun onMapSharedElements(
names: MutableList<String>,
sharedElements: MutableMap<String, View>
) {
if (isReturning) {
val currentPage = currentPage ?: return
sharedElements[names[0]] = currentPage
}
}
}
}
@@ -1,905 +0,0 @@
package net.vonforst.evmap.fragment
import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Color
import android.os.Bundle
import android.os.Handler
import android.view.*
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.annotation.RequiresPermission
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.PopupMenu
import androidx.core.app.ActivityCompat
import androidx.core.app.SharedElementCallback
import androidx.core.content.ContextCompat
import androidx.core.view.updateLayoutParams
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.Observer
import androidx.navigation.findNavController
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.transition.TransitionInflater
import androidx.transition.TransitionManager
import com.car2go.maps.AnyMap
import com.car2go.maps.MapFragment
import com.car2go.maps.OnMapReadyCallback
import com.car2go.maps.model.BitmapDescriptor
import com.car2go.maps.model.LatLng
import com.car2go.maps.model.Marker
import com.car2go.maps.model.MarkerOptions
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.transition.MaterialArcMotion
import com.google.android.material.transition.MaterialContainerTransform
import com.mahc.custombottomsheetbehavior.BottomSheetBehaviorGoogleMapsLike
import com.mahc.custombottomsheetbehavior.BottomSheetBehaviorGoogleMapsLike.STATE_COLLAPSED
import com.mahc.custombottomsheetbehavior.BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN
import com.mahc.custombottomsheetbehavior.MergedAppBarLayoutBehavior
import com.mapzen.android.lost.api.LocationServices
import com.mapzen.android.lost.api.LostApiClient
import io.michaelrocks.bimap.HashBiMap
import io.michaelrocks.bimap.MutableBiMap
import kotlinx.android.synthetic.main.fragment_map.*
import net.vonforst.evmap.*
import net.vonforst.evmap.adapter.ConnectorAdapter
import net.vonforst.evmap.adapter.DetailsAdapter
import net.vonforst.evmap.adapter.GalleryAdapter
import net.vonforst.evmap.api.goingelectric.ChargeLocation
import net.vonforst.evmap.api.goingelectric.ChargeLocationCluster
import net.vonforst.evmap.api.goingelectric.ChargepointListItem
import net.vonforst.evmap.autocomplete.handleAutocompleteResult
import net.vonforst.evmap.autocomplete.launchAutocomplete
import net.vonforst.evmap.databinding.FragmentMapBinding
import net.vonforst.evmap.storage.PreferenceDataSource
import net.vonforst.evmap.ui.ChargerIconGenerator
import net.vonforst.evmap.ui.ClusterIconGenerator
import net.vonforst.evmap.ui.MarkerAnimator
import net.vonforst.evmap.ui.getMarkerTint
import net.vonforst.evmap.viewmodel.*
const val REQUEST_AUTOCOMPLETE = 2
const val ARG_CHARGER_ID = "chargerId"
const val ARG_LAT = "lat"
const val ARG_LON = "lon"
class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallback,
LostApiClient.ConnectionCallbacks {
private lateinit var binding: FragmentMapBinding
private val vm: MapViewModel by viewModels(factoryProducer = {
viewModelFactory {
MapViewModel(
requireActivity().application,
getString(R.string.goingelectric_key)
)
}
})
private val galleryVm: GalleryViewModel by activityViewModels()
private lateinit var mapFragment: MapFragment
private var map: AnyMap? = null
private lateinit var locationClient: LostApiClient
private lateinit var bottomSheetBehavior: BottomSheetBehaviorGoogleMapsLike<View>
private lateinit var detailAppBarBehavior: MergedAppBarLayoutBehavior
private var markers: MutableBiMap<Marker, ChargeLocation> = HashBiMap()
private var clusterMarkers: List<Marker> = emptyList()
private var searchResultMarker: Marker? = null
private var searchResultIcon: BitmapDescriptor? = null
private var connectionErrorSnackbar: Snackbar? = null
private var previousChargepointIds: Set<Long>? = null
private lateinit var clusterIconGenerator: ClusterIconGenerator
private lateinit var chargerIconGenerator: ChargerIconGenerator
private lateinit var animator: MarkerAnimator
private lateinit var favToggle: MenuItem
private val backPressedCallback = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
val value = vm.layersMenuOpen.value
if (value != null && value) {
closeLayersMenu()
return
}
val state = bottomSheetBehavior.state
if (state != STATE_COLLAPSED && state != STATE_HIDDEN) {
bottomSheetBehavior.state = STATE_COLLAPSED
} else if (state == STATE_COLLAPSED) {
vm.chargerSparse.value = null
} else if (state == STATE_HIDDEN) {
vm.searchResult.value = null
}
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_map, container, false)
binding.lifecycleOwner = this
binding.vm = vm
mapFragment = MapFragment()
val provider = PreferenceDataSource(requireContext()).mapProvider
mapFragment.setPriority(
arrayOf(
when (provider) {
"mapbox" -> MapFragment.MAPBOX
"google" -> MapFragment.GOOGLE
else -> null
},
MapFragment.GOOGLE,
MapFragment.MAPBOX
)
)
requireActivity().supportFragmentManager
.beginTransaction()
.replace(R.id.map, mapFragment)
.commit()
// reset map-related stuff (map provider may have changed)
map = null
markers.clear()
clusterMarkers = emptyList()
searchResultMarker = null
searchResultIcon = null
locationClient = LostApiClient.Builder(requireContext())
.addConnectionCallbacks(this)
.build()
locationClient.connect()
clusterIconGenerator = ClusterIconGenerator(requireContext())
setHasOptionsMenu(true)
postponeEnterTransition()
binding.root.setOnApplyWindowInsetsListener { v, insets ->
binding.detailAppBar.toolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = insets.systemWindowInsetTop
}
insets
}
setExitSharedElementCallback(exitElementCallback)
exitTransition = TransitionInflater.from(requireContext())
.inflateTransition(R.transition.map_exit_transition)
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
backPressedCallback
)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
mapFragment.getMapAsync(this)
bottomSheetBehavior = BottomSheetBehaviorGoogleMapsLike.from(binding.bottomSheet)
detailAppBarBehavior = MergedAppBarLayoutBehavior.from(binding.detailAppBar)
binding.detailAppBar.toolbar.inflateMenu(R.menu.detail)
favToggle = binding.detailAppBar.toolbar.menu.findItem(R.id.menu_fav)
setupObservers()
setupClickListeners()
setupAdapters()
val navController = findNavController()
(activity as? MapsActivity)?.setSupportActionBar(binding.toolbar)
binding.toolbar.setupWithNavController(
navController,
(requireActivity() as MapsActivity).appBarConfiguration
)
}
override fun onResume() {
super.onResume()
val hostActivity = activity as? MapsActivity ?: return
hostActivity.fragmentCallback = this
}
private fun setupClickListeners() {
binding.fabLocate.setOnClickListener {
if (ContextCompat.checkSelfPermission(
requireContext(),
ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
requestPermissions(
arrayOf(ACCESS_FINE_LOCATION),
REQUEST_LOCATION_PERMISSION
)
} else {
enableLocation(moveTo = true, animate = true)
}
}
binding.fabDirections.setOnClickListener {
val charger = vm.charger.value?.data
if (charger != null) {
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
(requireActivity() as MapsActivity).navigateTo(charger)
}
}
}
binding.fabLayers.setOnClickListener {
openLayersMenu()
}
binding.detailView.goingelectricButton.setOnClickListener {
val charger = vm.charger.value?.data
if (charger != null) {
(activity as? MapsActivity)?.openUrl("https:${charger.url}")
}
}
binding.detailView.btnChargeprice.setOnClickListener {
val charger = vm.charger.value?.data ?: return@setOnClickListener
(activity as? MapsActivity)?.openUrl(
"https://www.chargeprice.app/?poi_id=${charger.id}&poi_source=going_electric")
}
binding.detailView.topPart.setOnClickListener {
bottomSheetBehavior.state = BottomSheetBehaviorGoogleMapsLike.STATE_ANCHOR_POINT
}
binding.search.setOnClickListener {
launchAutocomplete(this)
}
binding.detailAppBar.toolbar.setNavigationOnClickListener {
bottomSheetBehavior.state = STATE_COLLAPSED
}
binding.detailAppBar.toolbar.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_fav -> {
toggleFavorite()
true
}
R.id.menu_share -> {
val charger = vm.charger.value?.data
if (charger != null) {
(activity as? MapsActivity)?.shareUrl("https:${charger.url}")
}
true
}
else -> false
}
}
}
private fun openLayersMenu() {
binding.fabLayers.tag = false
val materialTransform = MaterialContainerTransform().apply {
startView = binding.fabLayers
endView = binding.layersSheet
setPathMotion(MaterialArcMotion())
duration = 250
scrimColor = Color.TRANSPARENT
}
TransitionManager.beginDelayedTransition(binding.root, materialTransform)
vm.layersMenuOpen.value = true
}
private fun closeLayersMenu() {
binding.fabLayers.tag = true
val materialTransform = MaterialContainerTransform().apply {
startView = binding.layersSheet
endView = binding.fabLayers
setPathMotion(MaterialArcMotion())
duration = 200
scrimColor = Color.TRANSPARENT
}
TransitionManager.beginDelayedTransition(binding.root, materialTransform)
vm.layersMenuOpen.value = false
}
private fun toggleFavorite() {
val favs = vm.favorites.value ?: return
val charger = vm.chargerSparse.value ?: return
if (favs.find { it.id == charger.id } != null) {
vm.deleteFavorite(charger)
} else {
vm.insertFavorite(charger)
}
}
private fun setupObservers() {
bottomSheetBehavior.addBottomSheetCallback(object :
BottomSheetBehaviorGoogleMapsLike.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
vm.bottomSheetState.value = newState
updateBackPressedCallback()
if (vm.layersMenuOpen.value!! && newState !in listOf(BottomSheetBehaviorGoogleMapsLike.STATE_SETTLING, BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN, BottomSheetBehaviorGoogleMapsLike.STATE_COLLAPSED)) {
closeLayersMenu()
}
}
})
vm.chargerSparse.observe(viewLifecycleOwner, Observer {
if (it != null) {
if (vm.bottomSheetState.value != BottomSheetBehaviorGoogleMapsLike.STATE_ANCHOR_POINT) {
bottomSheetBehavior.state = STATE_COLLAPSED
}
binding.fabDirections.show()
detailAppBarBehavior.setToolbarTitle(it.name)
updateFavoriteToggle()
highlightMarker(it)
} else {
bottomSheetBehavior.state = STATE_HIDDEN
unhighlightAllMarkers()
}
})
vm.chargepoints.observe(viewLifecycleOwner, Observer { res ->
when (res.status) {
Status.ERROR -> {
val view = view ?: return@Observer
connectionErrorSnackbar?.dismiss()
connectionErrorSnackbar = Snackbar
.make(view, R.string.connection_error, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.retry) {
connectionErrorSnackbar?.dismiss()
vm.reloadChargepoints()
}
connectionErrorSnackbar!!.show()
}
Status.SUCCESS -> {
connectionErrorSnackbar?.dismiss()
}
Status.LOADING -> {
}
}
val chargepoints = res.data
if (chargepoints != null) {
updateMap(chargepoints)
}
})
vm.favorites.observe(viewLifecycleOwner, Observer {
updateFavoriteToggle()
})
vm.searchResult.observe(viewLifecycleOwner, Observer { place ->
val map = this.map ?: return@Observer
searchResultMarker?.remove()
searchResultMarker = null
if (place != null) {
if (place.viewport != null) {
map.animateCamera(map.cameraUpdateFactory.newLatLngBounds(place.viewport, 0))
} else {
map.animateCamera(map.cameraUpdateFactory.newLatLngZoom(place.latLng, 12f))
}
if (searchResultIcon == null) {
searchResultIcon =
map.bitmapDescriptorFactory.fromResource(R.drawable.ic_map_marker)
}
searchResultMarker = map.addMarker(
MarkerOptions()
.position(place.latLng)
.icon(searchResultIcon)
.anchor(0.5f, 1f)
)
}
updateBackPressedCallback()
})
vm.layersMenuOpen.observe(viewLifecycleOwner, Observer { open ->
binding.fabLayers.visibility = if (open) View.GONE else View.VISIBLE
binding.layersSheet.visibility = if (open) View.VISIBLE else View.GONE
updateBackPressedCallback()
})
vm.mapType.observe(viewLifecycleOwner, Observer {
map?.setMapType(it)
})
vm.mapTrafficEnabled.observe(viewLifecycleOwner, Observer {
map?.setTrafficEnabled(it)
})
updateBackPressedCallback()
}
private fun updateBackPressedCallback() {
backPressedCallback.isEnabled =
vm.bottomSheetState.value != null && vm.bottomSheetState.value != STATE_HIDDEN
|| vm.searchResult.value != null
|| (vm.layersMenuOpen.value ?: false)
}
private fun unhighlightAllMarkers() {
markers.forEach { (m, c) ->
m.setIcon(
chargerIconGenerator.getBitmapDescriptor(
getMarkerTint(c, vm.filteredConnectors.value), fault = c.faultReport != null
)
)
}
}
private fun highlightMarker(charger: ChargeLocation) {
val marker = markers.inverse[charger] ?: return
// highlight this marker
marker.setIcon(
chargerIconGenerator.getBitmapDescriptor(
getMarkerTint(charger, vm.filteredConnectors.value),
highlight = true,
fault = charger.faultReport != null
)
)
animator.animateMarkerBounce(marker)
// un-highlight all other markers
markers.forEach { (m, c) ->
if (m != marker) {
m.setIcon(
chargerIconGenerator.getBitmapDescriptor(
getMarkerTint(c, vm.filteredConnectors.value), fault = c.faultReport != null
)
)
}
}
}
private fun updateFavoriteToggle() {
val favs = vm.favorites.value ?: return
val charger = vm.chargerSparse.value ?: return
if (favs.find { it.id == charger.id } != null) {
favToggle.setIcon(R.drawable.ic_fav)
} else {
favToggle.setIcon(R.drawable.ic_fav_no)
}
}
private fun setupAdapters() {
val galleryClickListener = object : GalleryAdapter.ItemClickListener {
override fun onItemClick(view: View, position: Int) {
val photos = vm.charger.value?.data?.photos ?: return
val extras = FragmentNavigatorExtras(view to view.transitionName)
view.findNavController().navigate(
R.id.action_map_to_galleryFragment,
GalleryFragment.buildArgs(photos, position),
null,
extras
)
}
}
val galleryPosition = galleryVm.galleryPosition.value
binding.gallery.apply {
adapter = GalleryAdapter(context, galleryClickListener, pageToLoad = galleryPosition) {
startPostponedEnterTransition()
}
itemAnimator = null
layoutManager =
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
addItemDecoration(
DividerItemDecoration(
context, LinearLayoutManager.HORIZONTAL
).apply {
setDrawable(context.getDrawable(R.drawable.gallery_divider)!!)
})
}
if (galleryPosition == null) {
startPostponedEnterTransition()
} else {
binding.gallery.scrollToPosition(galleryPosition)
// make sure that the app does not freeze waiting for a picture to load
Handler().postDelayed({
startPostponedEnterTransition()
}, 500)
}
binding.detailView.connectors.apply {
adapter = ConnectorAdapter()
itemAnimator = null
layoutManager =
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
}
binding.detailView.details.apply {
adapter = DetailsAdapter().apply {
onClickListener = {
val charger = vm.chargerDetails.value?.data
if (charger != null) {
when (it.icon) {
R.drawable.ic_location -> {
(activity as? MapsActivity)?.showLocation(charger)
}
R.drawable.ic_fault_report -> {
(activity as? MapsActivity)?.openUrl("https:${charger.url}")
}
R.drawable.ic_payment -> {
showPaymentMethodsDialog(charger)
}
}
}
}
}
itemAnimator = null
layoutManager =
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
addItemDecoration(
DividerItemDecoration(
context,
LinearLayoutManager.VERTICAL
)
)
}
}
private fun showPaymentMethodsDialog(charger: ChargeLocation) {
val activity = activity ?: return
val chargecardData = vm.chargeCardMap.value ?: return
val chargecards = charger.chargecards ?: return
val filteredChargeCards = vm.filteredChargeCards.value
val data = chargecards.mapNotNull { chargecardData[it.id] }
.sortedBy { it.name }
.sortedByDescending { filteredChargeCards?.contains(it.id) }
val names = data.map {
if (filteredChargeCards?.contains(it.id) == true) {
it.name.bold()
} else {
it.name
}
}
AlertDialog.Builder(activity)
.setTitle(R.string.charge_cards)
.setItems(names.toTypedArray()) { _, i ->
val card = data[i]
(activity as? MapsActivity)?.openUrl("https:${card.url}")
}.show()
}
override fun onMapReady(map: AnyMap) {
this.map = map
chargerIconGenerator = ChargerIconGenerator(requireContext(), map.bitmapDescriptorFactory)
animator = MarkerAnimator(chargerIconGenerator)
map.uiSettings.setTiltGesturesEnabled(false)
map.setIndoorEnabled(false)
map.uiSettings.setIndoorLevelPickerEnabled(false)
map.setOnCameraIdleListener {
vm.mapPosition.value = MapPosition(
map.projection.visibleRegion.latLngBounds, map.cameraPosition.zoom
)
}
map.setOnMarkerClickListener { marker ->
when (marker) {
in markers -> {
vm.chargerSparse.value = markers[marker]
true
}
in clusterMarkers -> {
val newZoom = map.cameraPosition.zoom + 2
map.animateCamera(
map.cameraUpdateFactory.newLatLngZoom(
marker.position,
newZoom
)
)
true
}
else -> false
}
}
map.setOnMapClickListener {
if (backPressedCallback.isEnabled) {
backPressedCallback.handleOnBackPressed()
}
}
// set padding so that compass is not obstructed by toolbar
map.setPadding(0, binding.toolbarContainer.height, 0, 0)
val mode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
map.setMapStyle(
if (mode == Configuration.UI_MODE_NIGHT_YES) AnyMap.Style.DARK else AnyMap.Style.NORMAL
)
val position = vm.mapPosition.value
val lat = arguments?.optDouble(ARG_LAT)
val lon = arguments?.optDouble(ARG_LON)
var positionSet = false
if (position != null) {
val cameraUpdate =
map.cameraUpdateFactory.newLatLngZoom(position.bounds.center, position.zoom)
map.moveCamera(cameraUpdate)
positionSet = true
} else if (lat != null && lon != null) {
// show given position
val cameraUpdate = map.cameraUpdateFactory.newLatLngZoom(LatLng(lat, lon), 16f)
map.moveCamera(cameraUpdate)
// show charger detail after chargers were loaded
val chargerId = arguments?.optLong(ARG_CHARGER_ID)
vm.chargepoints.observe(
viewLifecycleOwner,
object : Observer<Resource<List<ChargepointListItem>>> {
override fun onChanged(res: Resource<List<ChargepointListItem>>) {
if (res.data == null) return
for (item in res.data) {
if (item is ChargeLocation && item.id == chargerId) {
vm.chargerSparse.value = item
vm.chargepoints.removeObserver(this)
}
}
}
})
positionSet = true
}
if (ContextCompat.checkSelfPermission(
requireContext(),
ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
enableLocation(!positionSet, false)
positionSet = true
}
if (!positionSet) {
// center the camera on Europe
val cameraUpdate =
map.cameraUpdateFactory.newLatLngZoom(LatLng(50.113388, 9.252536), 3.5f)
map.moveCamera(cameraUpdate)
}
vm.mapPosition.value = MapPosition(
map.projection.visibleRegion.latLngBounds, map.cameraPosition.zoom
)
}
@RequiresPermission(ACCESS_FINE_LOCATION)
private fun enableLocation(moveTo: Boolean, animate: Boolean) {
val map = this.map ?: return
map.setMyLocationEnabled(true)
vm.myLocationEnabled.value = true
map.uiSettings.setMyLocationButtonEnabled(false)
if (moveTo && locationClient.isConnected) {
moveToCurrentLocation(map, animate)
}
}
@RequiresPermission(ACCESS_FINE_LOCATION)
private fun moveToCurrentLocation(map: AnyMap, animate: Boolean) {
val location = LocationServices.FusedLocationApi.getLastLocation(locationClient)
if (location != null) {
val latLng = LatLng(location.latitude, location.longitude)
val camUpdate = map.cameraUpdateFactory.newLatLngZoom(latLng, 13f)
if (animate) {
map.animateCamera(camUpdate)
} else {
map.moveCamera(camUpdate)
}
}
}
@Synchronized
private fun updateMap(chargepoints: List<ChargepointListItem>) {
val map = this.map ?: return
clusterMarkers.forEach { it.remove() }
val clusters = chargepoints.filterIsInstance<ChargeLocationCluster>()
val chargers = chargepoints.filterIsInstance<ChargeLocation>()
val chargepointIds = chargers.map { it.id }.toSet()
// update icons of existing markers (connector filter may have changed)
for ((marker, charger) in markers) {
marker.setIcon(
chargerIconGenerator.getBitmapDescriptor(
getMarkerTint(charger, vm.filteredConnectors.value),
highlight = charger == vm.chargerSparse.value,
fault = charger.faultReport != null
)
)
}
if (chargers.toSet() != markers.values) {
// remove markers that disappeared
val bounds = map.projection.visibleRegion.latLngBounds
markers.entries.toList().forEach {
val marker = it.key
val charger = it.value
if (!chargepointIds.contains(charger.id)) {
// animate marker if it is visible, otherwise remove immediately
if (bounds.contains(marker.position)) {
val tint = getMarkerTint(charger, vm.filteredConnectors.value)
val highlight = charger == vm.chargerSparse.value
val fault = charger.faultReport != null
animator.animateMarkerDisappear(marker, tint, highlight, fault)
} else {
animator.deleteMarker(marker)
}
markers.remove(marker)
}
}
// add new markers
val map1 = markers.values.map { it.id }
for (charger in chargers) {
if (!map1.contains(charger.id)) {
val tint = getMarkerTint(charger, vm.filteredConnectors.value)
val highlight = charger == vm.chargerSparse.value
val fault = charger.faultReport != null
val marker = map.addMarker(
MarkerOptions()
.position(LatLng(charger.coordinates.lat, charger.coordinates.lng))
.icon(
chargerIconGenerator.getBitmapDescriptor(
tint,
0,
255,
highlight,
fault
)
)
.anchor(0.5f, 1f)
)
animator.animateMarkerAppear(marker, tint, highlight, fault)
markers[marker] = charger
}
}
previousChargepointIds = chargepointIds
}
clusterMarkers = clusters.map { cluster ->
map.addMarker(
MarkerOptions()
.position(LatLng(cluster.coordinates.lat, cluster.coordinates.lng))
.icon(
map.bitmapDescriptorFactory.fromBitmap(
clusterIconGenerator.makeIcon(
cluster.clusterCount.toString()
)
)
)
.anchor(0.5f, 0.5f)
)
}
}
@SuppressLint("MissingPermission")
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
when (requestCode) {
REQUEST_LOCATION_PERMISSION -> {
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
enableLocation(moveTo = true, animate = true)
}
}
else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.map, menu)
val filterItem = menu.findItem(R.id.menu_filter)
val filterView = filterItem.actionView
val filterBadge = filterView?.findViewById<TextView>(R.id.filter_badge)
if (filterBadge != null) {
// set up badge showing number of active filters
vm.filtersCount.observe(viewLifecycleOwner, Observer {
filterBadge.visibility = if (it > 0) View.VISIBLE else View.GONE
filterBadge.text = it.toString()
})
}
filterView?.setOnClickListener {
val popup = PopupMenu(requireContext(), it, Gravity.END)
popup.menuInflater.inflate(R.menu.popup_filter, popup.menu)
popup.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_edit_filters -> {
requireView().findNavController().navigate(
R.id.action_map_to_filterFragment
)
true
}
R.id.menu_filters_active -> {
vm.filtersActive.value = !vm.filtersActive.value!!
true
}
else -> false
}
}
val checkItem = popup.menu.findItem(R.id.menu_filters_active)
vm.filtersActive.observe(viewLifecycleOwner, Observer {
checkItem.isChecked = it
})
popup.show()
}
filterView?.setOnLongClickListener {
// enable/disable filters
vm.filtersActive.value = !vm.filtersActive.value!!
// haptic feedback
filterView.performHapticFeedback(
HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
)
// show snackbar
Snackbar.make(
requireView(), if (vm.filtersActive.value!!) {
R.string.filters_activated
} else {
R.string.filters_deactivated
}, Snackbar.LENGTH_SHORT
).show()
true
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
REQUEST_AUTOCOMPLETE -> {
if (resultCode == Activity.RESULT_OK && data != null) {
vm.searchResult.value = handleAutocompleteResult(data)
}
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
override fun getRootView(): View {
return root
}
private val exitElementCallback: SharedElementCallback = object : SharedElementCallback() {
override fun onMapSharedElements(
names: MutableList<String>,
sharedElements: MutableMap<String, View>
) {
// Locate the ViewHolder for the clicked position.
val position = galleryVm.galleryPosition.value ?: return
val vh = binding.gallery.findViewHolderForAdapterPosition(position)
if (vh?.itemView == null) return
// Map the first shared element name to the child ImageView.
sharedElements[names[0]] = vh.itemView
}
}
companion object {
fun showCharger(charger: ChargeLocation): Bundle {
return Bundle().apply {
putLong(ARG_CHARGER_ID, charger.id)
putDouble(ARG_LAT, charger.coordinates.lat)
putDouble(ARG_LON, charger.coordinates.lng)
}
}
}
override fun onConnected() {
val map = this.map ?: return
if (vm.myLocationEnabled.value == true) {
if (ActivityCompat.checkSelfPermission(
requireContext(),
ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
moveToCurrentLocation(map, false)
}
}
}
override fun onConnectionSuspended() {
}
}
@@ -1,121 +0,0 @@
package net.vonforst.evmap.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.core.widget.doAfterTextChanged
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.dialog_multi_select.*
import net.vonforst.evmap.R
import net.vonforst.evmap.adapter.DataBindingAdapter
import net.vonforst.evmap.adapter.Equatable
import java.util.*
import kotlin.collections.HashMap
import kotlin.collections.HashSet
class MultiSelectDialog : AppCompatDialogFragment() {
companion object {
fun getInstance(
title: String,
data: Map<String, String>,
selected: Set<String>,
commonChoices: Set<String>?
): MultiSelectDialog {
val dialog = MultiSelectDialog()
dialog.arguments = Bundle().apply {
putString("title", title)
putSerializable("data", HashMap(data))
putSerializable("selected", HashSet(selected))
if (commonChoices != null) putSerializable("commonChoices", HashSet(commonChoices))
}
return dialog
}
}
var okListener: ((Set<String>) -> Unit)? = null
var cancelListener: (() -> Unit)? = null
private lateinit var items: List<MultiSelectItem>
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.dialog_multi_select, container)
}
override fun onStart() {
super.onStart()
// dialog with 95% screen height
dialog?.window?.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
(resources.displayMetrics.heightPixels * 0.95).toInt()
)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val args = requireArguments()
val data = args.getSerializable("data") as HashMap<String, String>
val selected = args.getSerializable("selected") as HashSet<String>
val title = args.getString("title")
val commonChoices = if (args.containsKey("commonChoices")) {
args.getSerializable("commonChoices") as HashSet<String>
} else null
dialogTitle.text = title
val adapter = Adapter()
list.adapter = adapter
list.layoutManager = LinearLayoutManager(view.context)
items = data.entries.toList()
.sortedBy { it.value }
.sortedByDescending { commonChoices?.contains(it.key) == true }
.map { MultiSelectItem(it.key, it.value, it.key in selected) }
adapter.submitList(items)
etSearch.doAfterTextChanged { text ->
adapter.submitList(search(items, text.toString()))
}
btnCancel.setOnClickListener {
cancelListener?.let { listener ->
listener()
}
dismiss()
}
btnOK.setOnClickListener {
okListener?.let { listener ->
val result = items.filter { it.selected }.map { it.key }.toSet()
listener(result)
}
dismiss()
}
btnAll.setOnClickListener {
items = items.map { MultiSelectItem(it.key, it.name, true) }
adapter.submitList(search(items, etSearch.text.toString()))
}
btnNone.setOnClickListener {
items = items.map { MultiSelectItem(it.key, it.name, false) }
adapter.submitList(search(items, etSearch.text.toString()))
}
}
}
private fun search(
items: List<MultiSelectItem>,
text: String
): List<MultiSelectItem> {
return items.filter { item ->
// search for string within name
text.toLowerCase(Locale.getDefault()) in item.name.toLowerCase(Locale.getDefault())
}
}
class Adapter() : DataBindingAdapter<MultiSelectItem>({ it.key }) {
override fun getItemViewType(position: Int) = R.layout.dialog_multi_select_item
}
data class MultiSelectItem(val key: String, val name: String, var selected: Boolean) : Equatable
@@ -1,68 +0,0 @@
package net.vonforst.evmap.fragment
import android.content.SharedPreferences
import android.os.Bundle
import android.view.View
import androidx.appcompat.widget.Toolbar
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import net.vonforst.evmap.MapsActivity
import net.vonforst.evmap.R
import net.vonforst.evmap.storage.PreferenceDataSource
import net.vonforst.evmap.ui.updateNightMode
class SettingsFragment : PreferenceFragmentCompat(),
SharedPreferences.OnSharedPreferenceChangeListener {
private lateinit var prefs: PreferenceDataSource
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val toolbar = view.findViewById(R.id.toolbar) as Toolbar
prefs = PreferenceDataSource(requireContext())
val navController = findNavController()
toolbar.setupWithNavController(
navController,
(requireActivity() as MapsActivity).appBarConfiguration
)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.settings, rootKey)
}
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
return when (preference?.key) {
else -> super.onPreferenceTreeClick(preference)
}
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
when (key) {
"language" -> {
activity?.let {
it.finish();
it.startActivity(it.intent);
}
}
"darkmode" -> {
updateNightMode(prefs)
}
}
}
override fun onResume() {
super.onResume()
preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
}
override fun onPause() {
preferenceManager.sharedPreferences
.unregisterOnSharedPreferenceChangeListener(this)
super.onPause()
}
}
@@ -1,54 +0,0 @@
package net.vonforst.evmap.storage
import androidx.lifecycle.LiveData
import androidx.room.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import net.vonforst.evmap.api.goingelectric.ChargeCard
import net.vonforst.evmap.api.goingelectric.GoingElectricApi
import java.io.IOException
import java.time.Duration
import java.time.Instant
@Dao
interface ChargeCardDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(vararg chargeCards: ChargeCard)
@Delete
suspend fun delete(vararg chargeCards: ChargeCard)
@Query("SELECT * FROM chargeCard")
fun getAllChargeCards(): LiveData<List<ChargeCard>>
}
class ChargeCardRepository(
private val api: GoingElectricApi, private val scope: CoroutineScope,
private val dao: ChargeCardDao, private val prefs: PreferenceDataSource
) {
fun getChargeCards(): LiveData<List<ChargeCard>> {
scope.launch {
updateChargeCards()
}
return dao.getAllChargeCards()
}
private suspend fun updateChargeCards() {
if (Duration.between(prefs.lastChargeCardUpdate, Instant.now()) < Duration.ofDays(1)) return
try {
val response = api.getChargeCards()
if (!response.isSuccessful) return
for (card in response.body()!!.result) {
dao.insert(card)
}
prefs.lastChargeCardUpdate = Instant.now()
} catch (e: IOException) {
// ignore, and retry next time
e.printStackTrace()
return
}
}
}
@@ -1,17 +0,0 @@
package net.vonforst.evmap.storage
import androidx.lifecycle.LiveData
import androidx.room.*
import net.vonforst.evmap.api.goingelectric.ChargeLocation
@Dao
interface ChargeLocationsDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(vararg locations: ChargeLocation)
@Delete
suspend fun delete(vararg locations: ChargeLocation)
@Query("SELECT * FROM chargelocation")
fun getAllChargeLocations(): LiveData<List<ChargeLocation>>
}
@@ -1,124 +0,0 @@
package net.vonforst.evmap.storage
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import net.vonforst.evmap.api.goingelectric.ChargeCard
import net.vonforst.evmap.api.goingelectric.ChargeLocation
import net.vonforst.evmap.viewmodel.BooleanFilterValue
import net.vonforst.evmap.viewmodel.MultipleChoiceFilterValue
import net.vonforst.evmap.viewmodel.SliderFilterValue
@Database(
entities = [
ChargeLocation::class,
BooleanFilterValue::class,
MultipleChoiceFilterValue::class,
SliderFilterValue::class,
Plug::class,
Network::class,
ChargeCard::class
], version = 8
)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun chargeLocationsDao(): ChargeLocationsDao
abstract fun filterValueDao(): FilterValueDao
abstract fun plugDao(): PlugDao
abstract fun networkDao(): NetworkDao
abstract fun chargeCardDao(): ChargeCardDao
companion object {
private lateinit var context: Context
private val database: AppDatabase by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
Room.databaseBuilder(context, AppDatabase::class.java, "evmap.db")
.addMigrations(
MIGRATION_2, MIGRATION_3, MIGRATION_4, MIGRATION_5, MIGRATION_6,
MIGRATION_7, MIGRATION_8
)
.build()
}
fun getInstance(context: Context): AppDatabase {
this.context = context.applicationContext
return database
}
private val MIGRATION_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
// SQL for creating tables copied from build/generated/source/kapt/debug/net/vonforst/evmap/storage/AppDatbase_Impl
db.execSQL("CREATE TABLE IF NOT EXISTS `BooleanFilterValue` (`key` TEXT NOT NULL, `value` INTEGER NOT NULL, PRIMARY KEY(`key`))")
db.execSQL("CREATE TABLE IF NOT EXISTS `MultipleChoiceFilterValue` (`key` TEXT NOT NULL, `values` TEXT NOT NULL, `all` INTEGER NOT NULL, PRIMARY KEY(`key`))")
db.execSQL("CREATE TABLE IF NOT EXISTS `SliderFilterValue` (`key` TEXT NOT NULL, `value` INTEGER NOT NULL, PRIMARY KEY(`key`))");
}
}
private val MIGRATION_3 = object : Migration(2, 3) {
override fun migrate(db: SupportSQLiteDatabase) {
// recreate ChargeLocation table to make postcode nullable
db.beginTransaction()
try {
db.execSQL("CREATE TABLE `ChargeLocationNew` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `chargepoints` TEXT NOT NULL, `network` TEXT, `url` TEXT NOT NULL, `verified` INTEGER NOT NULL, `operator` TEXT, `generalInformation` TEXT, `amenities` TEXT, `locationDescription` TEXT, `photos` TEXT, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `city` TEXT NOT NULL, `country` TEXT NOT NULL, `postcode` TEXT, `street` TEXT NOT NULL, `twentyfourSeven` INTEGER, `description` TEXT, `mostart` TEXT, `moend` TEXT, `tustart` TEXT, `tuend` TEXT, `westart` TEXT, `weend` TEXT, `thstart` TEXT, `thend` TEXT, `frstart` TEXT, `frend` TEXT, `sastart` TEXT, `saend` TEXT, `sustart` TEXT, `suend` TEXT, `hostart` TEXT, `hoend` TEXT, `freecharging` INTEGER, `freeparking` INTEGER, `descriptionShort` TEXT, `descriptionLong` TEXT, PRIMARY KEY(`id`))");
db.execSQL("INSERT INTO `ChargeLocationNew` SELECT * FROM `ChargeLocation`");
db.execSQL("DROP TABLE `ChargeLocation`")
db.execSQL("ALTER TABLE `ChargeLocationNew` RENAME TO `ChargeLocation`")
db.setTransactionSuccessful()
} finally {
db.endTransaction()
}
}
}
private val MIGRATION_4 = object : Migration(3, 4) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("CREATE TABLE IF NOT EXISTS `Plug` (`name` TEXT NOT NULL, PRIMARY KEY(`name`))")
}
}
private val MIGRATION_5 = object : Migration(4, 5) {
override fun migrate(db: SupportSQLiteDatabase) {
// recreate ChargeLocation table to make other address fields nullable
db.beginTransaction()
try {
db.execSQL("CREATE TABLE `ChargeLocationNew` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `chargepoints` TEXT NOT NULL, `network` TEXT, `url` TEXT NOT NULL, `verified` INTEGER NOT NULL, `operator` TEXT, `generalInformation` TEXT, `amenities` TEXT, `locationDescription` TEXT, `photos` TEXT, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `city` TEXT, `country` TEXT, `postcode` TEXT, `street` TEXT, `twentyfourSeven` INTEGER, `description` TEXT, `mostart` TEXT, `moend` TEXT, `tustart` TEXT, `tuend` TEXT, `westart` TEXT, `weend` TEXT, `thstart` TEXT, `thend` TEXT, `frstart` TEXT, `frend` TEXT, `sastart` TEXT, `saend` TEXT, `sustart` TEXT, `suend` TEXT, `hostart` TEXT, `hoend` TEXT, `freecharging` INTEGER, `freeparking` INTEGER, `descriptionShort` TEXT, `descriptionLong` TEXT, PRIMARY KEY(`id`))");
db.execSQL("INSERT INTO `ChargeLocationNew` SELECT * FROM `ChargeLocation`");
db.execSQL("DROP TABLE `ChargeLocation`")
db.execSQL("ALTER TABLE `ChargeLocationNew` RENAME TO `ChargeLocation`")
db.setTransactionSuccessful()
} finally {
db.endTransaction()
}
}
}
private val MIGRATION_6 = object : Migration(5, 6) {
override fun migrate(db: SupportSQLiteDatabase) {
db.beginTransaction()
try {
db.execSQL("ALTER TABLE `ChargeLocation` ADD `fault_report_created` INTEGER")
db.execSQL("ALTER TABLE `ChargeLocation` ADD `fault_report_description` TEXT")
db.setTransactionSuccessful()
} finally {
db.endTransaction()
}
}
}
private val MIGRATION_7 = object : Migration(6, 7) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("CREATE TABLE IF NOT EXISTS `Network` (`name` TEXT NOT NULL, PRIMARY KEY(`name`))")
db.execSQL("CREATE TABLE IF NOT EXISTS `ChargeCard` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY(`id`))")
}
}
private val MIGRATION_8 = object : Migration(7, 8) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE `ChargeLocation` ADD `chargecards` TEXT")
}
}
}
}
@@ -1,55 +0,0 @@
package net.vonforst.evmap.storage
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.room.*
import net.vonforst.evmap.viewmodel.BooleanFilterValue
import net.vonforst.evmap.viewmodel.FilterValue
import net.vonforst.evmap.viewmodel.MultipleChoiceFilterValue
import net.vonforst.evmap.viewmodel.SliderFilterValue
@Dao
abstract class FilterValueDao {
@Query("SELECT * FROM booleanfiltervalue")
protected abstract fun getBooleanFilterValues(): LiveData<List<BooleanFilterValue>>
@Query("SELECT * FROM multiplechoicefiltervalue")
protected abstract fun getMultipleChoiceFilterValues(): LiveData<List<MultipleChoiceFilterValue>>
@Query("SELECT * FROM sliderfiltervalue")
protected abstract fun getSliderFilterValues(): LiveData<List<SliderFilterValue>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
protected abstract suspend fun insert(vararg values: BooleanFilterValue)
@Insert(onConflict = OnConflictStrategy.REPLACE)
protected abstract suspend fun insert(vararg values: MultipleChoiceFilterValue)
@Insert(onConflict = OnConflictStrategy.REPLACE)
protected abstract suspend fun insert(vararg values: SliderFilterValue)
open fun getFilterValues(): LiveData<List<FilterValue>> =
MediatorLiveData<List<FilterValue>>().apply {
val sources = listOf(
getBooleanFilterValues(),
getMultipleChoiceFilterValues(),
getSliderFilterValues()
)
for (source in sources) {
addSource(source) {
value = sources.mapNotNull { it.value }.flatten()
}
}
}
@Transaction
open suspend fun insert(vararg values: FilterValue) {
values.forEach {
when (it) {
is BooleanFilterValue -> insert(it)
is MultipleChoiceFilterValue -> insert(it)
is SliderFilterValue -> insert(it)
}
}
}
}
@@ -1,56 +0,0 @@
package net.vonforst.evmap.storage
import androidx.lifecycle.LiveData
import androidx.room.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import net.vonforst.evmap.api.goingelectric.GoingElectricApi
import java.io.IOException
import java.time.Duration
import java.time.Instant
@Entity
data class Network(@PrimaryKey val name: String)
@Dao
interface NetworkDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(vararg networks: Network)
@Delete
suspend fun delete(vararg networks: Network)
@Query("SELECT * FROM network")
fun getAllNetworks(): LiveData<List<Network>>
}
class NetworkRepository(
private val api: GoingElectricApi, private val scope: CoroutineScope,
private val dao: NetworkDao, private val prefs: PreferenceDataSource
) {
fun getNetworks(): LiveData<List<Network>> {
scope.launch {
updateNetworks()
}
return dao.getAllNetworks()
}
private suspend fun updateNetworks() {
if (Duration.between(prefs.lastNetworkUpdate, Instant.now()) < Duration.ofDays(1)) return
try {
val response = api.getNetworks()
if (!response.isSuccessful) return
for (name in response.body()!!.result) {
dao.insert(Network(name))
}
prefs.lastNetworkUpdate = Instant.now()
} catch (e: IOException) {
// ignore, and retry next time
e.printStackTrace()
return
}
}
}
@@ -1,56 +0,0 @@
package net.vonforst.evmap.storage
import androidx.lifecycle.LiveData
import androidx.room.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import net.vonforst.evmap.api.goingelectric.GoingElectricApi
import java.io.IOException
import java.time.Duration
import java.time.Instant
@Entity
data class Plug(@PrimaryKey val name: String)
@Dao
interface PlugDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(vararg plugs: Plug)
@Delete
suspend fun delete(vararg plugs: Plug)
@Query("SELECT * FROM plug")
fun getAllPlugs(): LiveData<List<Plug>>
}
class PlugRepository(
private val api: GoingElectricApi, private val scope: CoroutineScope,
private val dao: PlugDao, private val prefs: PreferenceDataSource
) {
fun getPlugs(): LiveData<List<Plug>> {
scope.launch {
updatePlugs()
}
return dao.getAllPlugs()
}
private suspend fun updatePlugs() {
if (Duration.between(prefs.lastPlugUpdate, Instant.now()) < Duration.ofDays(1)) return
try {
val response = api.getPlugs()
if (!response.isSuccessful) return
for (name in response.body()!!.result) {
dao.insert(Plug(name))
}
prefs.lastPlugUpdate = Instant.now()
} catch (e: IOException) {
// ignore, and retry next time
e.printStackTrace()
return
}
}
}
@@ -1,52 +0,0 @@
package net.vonforst.evmap.storage
import android.content.Context
import androidx.preference.PreferenceManager
import net.vonforst.evmap.R
import java.time.Instant
class PreferenceDataSource(val context: Context) {
val sp = PreferenceManager.getDefaultSharedPreferences(context)
var navigateUseMaps: Boolean
get() = sp.getBoolean("navigate_use_maps", true)
set(value) {
sp.edit().putBoolean("navigate_use_maps", value).apply()
}
var lastPlugUpdate: Instant
get() = Instant.ofEpochMilli(sp.getLong("last_plug_update", 0L))
set(value) {
sp.edit().putLong("last_plug_update", value.toEpochMilli()).apply()
}
var lastNetworkUpdate: Instant
get() = Instant.ofEpochMilli(sp.getLong("last_network_update", 0L))
set(value) {
sp.edit().putLong("last_network_update", value.toEpochMilli()).apply()
}
var lastChargeCardUpdate: Instant
get() = Instant.ofEpochMilli(sp.getLong("last_chargecard_update", 0L))
set(value) {
sp.edit().putLong("last_chargecard_update", value.toEpochMilli()).apply()
}
var filtersActive: Boolean
get() = sp.getBoolean("filters_active", true)
set(value) {
sp.edit().putBoolean("filters_active", value).apply()
}
val language: String
get() = sp.getString("language", "default")!!
val darkmode: String
get() = sp.getString("darkmode", "default")!!
val mapProvider: String
get() = sp.getString(
"map_provider",
context.getString(R.string.pref_map_provider_default)
)!!
}
@@ -1,94 +0,0 @@
package net.vonforst.evmap.storage
import androidx.room.TypeConverter
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import net.vonforst.evmap.api.goingelectric.ChargeCardId
import net.vonforst.evmap.api.goingelectric.Chargepoint
import net.vonforst.evmap.api.goingelectric.ChargerPhoto
import java.time.Instant
import java.time.LocalTime
class Converters {
val moshi = Moshi.Builder().build()
private val chargepointListAdapter by lazy {
val type = Types.newParameterizedType(List::class.java, Chargepoint::class.java)
moshi.adapter<List<Chargepoint>>(type)
}
private val chargerPhotoListAdapter by lazy {
val type = Types.newParameterizedType(List::class.java, ChargerPhoto::class.java)
moshi.adapter<List<ChargerPhoto>>(type)
}
private val chargeCardIdListAdapter by lazy {
val type = Types.newParameterizedType(List::class.java, ChargeCardId::class.java)
moshi.adapter<List<ChargeCardId>>(type)
}
private val stringSetAdapter by lazy {
val type = Types.newParameterizedType(Set::class.java, String::class.java)
moshi.adapter<Set<String>>(type)
}
@TypeConverter
fun fromChargepointList(value: List<Chargepoint>?): String {
return chargepointListAdapter.toJson(value)
}
@TypeConverter
fun toChargepointList(value: String): List<Chargepoint>? {
return chargepointListAdapter.fromJson(value)
}
@TypeConverter
fun fromChargerPhotoList(value: List<ChargerPhoto>?): String {
return chargerPhotoListAdapter.toJson(value)
}
@TypeConverter
fun toChargerPhotoList(value: String): List<ChargerPhoto>? {
return chargerPhotoListAdapter.fromJson(value)
}
@TypeConverter
fun fromChargeCardIdList(value: List<ChargeCardId>?): String {
return chargeCardIdListAdapter.toJson(value)
}
@TypeConverter
fun toChargeCardIdList(value: String?): List<ChargeCardId>? {
return value?.let { chargeCardIdListAdapter.fromJson(it) }
}
@TypeConverter
fun fromLocalTime(value: LocalTime?): String? {
return value?.toString()
}
@TypeConverter
fun toLocalTime(value: String?): LocalTime? {
return value?.let {
LocalTime.parse(it)
}
}
@TypeConverter
fun fromInstant(value: Instant?): Long? {
return value?.toEpochMilli()
}
@TypeConverter
fun toInstant(value: Long?): Instant? {
return value?.let {
Instant.ofEpochMilli(it)
}
}
@TypeConverter
fun fromStringSet(value: Set<String>?): String {
return stringSetAdapter.toJson(value)
}
@TypeConverter
fun toStringSet(value: String): Set<String>? {
return stringSetAdapter.fromJson(value)
}
}
@@ -1,55 +0,0 @@
package net.vonforst.evmap.ui
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.view.View
import android.view.ViewAnimationUtils
import android.view.animation.DecelerateInterpolator
import kotlin.math.hypot
fun View.startCircularReveal() {
addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
override fun onLayoutChange(
v: View, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int,
oldRight: Int, oldBottom: Int
) {
v.removeOnLayoutChangeListener(this)
val cx = v.right
val cy = v.top
val radius = hypot(right.toDouble(), bottom.toDouble()).toInt()
ViewAnimationUtils.createCircularReveal(v, cx, cy, 0f, radius.toFloat()).apply {
interpolator = DecelerateInterpolator(2f)
duration = 1000
start()
}
}
})
}
fun View.exitCircularReveal(block: () -> Unit) {
val startRadius = hypot(this.width.toDouble(), this.height.toDouble())
ViewAnimationUtils.createCircularReveal(this, this.width, 0, startRadius.toFloat(), 0f).apply {
duration = 350
interpolator = DecelerateInterpolator(1f)
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
visibility = View.INVISIBLE
block()
super.onAnimationEnd(animation)
}
})
start()
}
}
/**
* @return the position of the current [View]'s center in the screen
*/
fun View.findLocationOfCenterOnTheScreen(): IntArray {
val positions = intArrayOf(0, 0)
getLocationInWindow(positions)
// Get the center of the view
positions[0] = positions[0] + width / 2
positions[1] = positions[1] + height / 2
return positions
}
@@ -1,163 +0,0 @@
package net.vonforst.evmap.ui
import android.content.Context
import android.content.res.ColorStateList
import android.view.View
import android.view.ViewGroup.MarginLayoutParams
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.content.res.use
import androidx.core.text.HtmlCompat
import androidx.databinding.BindingAdapter
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.floatingactionbutton.FloatingActionButton
import net.vonforst.evmap.R
import net.vonforst.evmap.api.availability.ChargepointStatus
import net.vonforst.evmap.api.goingelectric.Chargepoint
import kotlin.math.roundToInt
@BindingAdapter("goneUnless")
fun goneUnless(view: View, visible: Boolean) {
view.visibility = if (visible) View.VISIBLE else View.GONE
}
@BindingAdapter("invisibleUnless")
fun invisibleUnless(view: View, visible: Boolean) {
view.visibility = if (visible) View.VISIBLE else View.INVISIBLE
}
@BindingAdapter("isFabActive")
fun isFabActive(view: FloatingActionButton, isColored: Boolean) {
val color = view.context.theme.obtainStyledAttributes(
intArrayOf(
if (isColored) {
R.attr.colorAccent
} else {
R.attr.colorControlNormal
}
)
)
view.imageTintList = ColorStateList.valueOf(color.getColor(0, 0))
}
@BindingAdapter("data")
fun <T> setRecyclerViewData(recyclerView: RecyclerView, items: List<T>?) {
if (recyclerView.adapter is ListAdapter<*, *>) {
(recyclerView.adapter as ListAdapter<T, *>).submitList(items)
}
}
@BindingAdapter("data")
fun <T> setRecyclerViewData(recyclerView: ViewPager2, items: List<T>?) {
if (recyclerView.adapter is ListAdapter<*, *>) {
(recyclerView.adapter as ListAdapter<T, *>).submitList(items)
}
}
@BindingAdapter("connectorIcon")
fun getConnectorItem(view: ImageView, type: String) {
view.setImageResource(
when (type) {
Chargepoint.CCS -> R.drawable.ic_connector_ccs
Chargepoint.CHADEMO -> R.drawable.ic_connector_chademo
Chargepoint.SCHUKO -> R.drawable.ic_connector_schuko
Chargepoint.SUPERCHARGER -> R.drawable.ic_connector_supercharger
Chargepoint.TYPE_2 -> R.drawable.ic_connector_typ2
Chargepoint.CEE_BLAU -> R.drawable.ic_connector_cee_blau
Chargepoint.CEE_ROT -> R.drawable.ic_connector_cee_rot
Chargepoint.TYPE_1 -> R.drawable.ic_connector_typ1
// TODO: add other connectors
else -> 0
}
)
}
@BindingAdapter("srcCompat")
fun setImageResource(imageView: ImageView, resource: Int) {
imageView.setImageResource(resource)
}
@BindingAdapter("android:contentDescription")
fun setContentDescriptionResource(imageView: ImageView, resource: Int) {
imageView.contentDescription = imageView.context.getString(resource)
}
@BindingAdapter("tintAvailability")
fun setImageTintAvailability(view: ImageView, available: List<ChargepointStatus>?) {
view.imageTintList = ColorStateList.valueOf(availabilityColor(available, view.context))
}
@BindingAdapter("textColorAvailability")
fun setTextColorAvailability(view: TextView, available: List<ChargepointStatus>?) {
view.setTextColor(availabilityColor(available, view.context))
}
@BindingAdapter("backgroundTintAvailability")
fun setBackgroundTintAvailability(view: View, available: List<ChargepointStatus>?) {
view.backgroundTintList = ColorStateList.valueOf(availabilityColor(available, view.context))
}
@BindingAdapter("selectableItemBackground")
fun applySelectableItemBackground(view: View, apply: Boolean) {
if (apply) {
view.context.obtainStyledAttributes(intArrayOf(R.attr.selectableItemBackground)).use {
view.background = it.getDrawable(0)
}
} else {
view.background = null
}
}
@BindingAdapter("htmlText")
fun setHtmlTextValue(textView: TextView, htmlText: String?) {
if (htmlText == null) {
textView.text = null
} else {
textView.text = HtmlCompat.fromHtml(htmlText, HtmlCompat.FROM_HTML_MODE_LEGACY)
}
}
@BindingAdapter("android:layout_marginTop")
fun setTopMargin(view: View, topMargin: Float) {
val layoutParams = view.layoutParams as MarginLayoutParams
layoutParams.setMargins(
layoutParams.leftMargin, topMargin.roundToInt(),
layoutParams.rightMargin, layoutParams.bottomMargin
)
view.layoutParams = layoutParams
}
private fun availabilityColor(
status: List<ChargepointStatus>?,
context: Context
): Int = if (status != null) {
val unknown = status.any { it == ChargepointStatus.UNKNOWN }
val available = status.count { it == ChargepointStatus.AVAILABLE }
if (unknown) {
ContextCompat.getColor(context, R.color.unknown)
} else if (available > 0) {
ContextCompat.getColor(context, R.color.available)
} else {
ContextCompat.getColor(context, R.color.unavailable)
}
} else {
val ta = context.theme.obtainStyledAttributes(intArrayOf(R.attr.colorControlNormal))
ta.getColor(0, 0)
}
fun availabilityText(status: List<ChargepointStatus>?): String? {
if (status == null) return null
val total = status.size
val unknown = status.count { it == ChargepointStatus.UNKNOWN }
val available = status.count { it == ChargepointStatus.AVAILABLE }
return if (unknown > 0) {
if (unknown == total) "?" else "$available?"
} else available.toString()
}
@@ -1,41 +0,0 @@
package net.vonforst.evmap.ui;
import com.car2go.maps.model.LatLng
import com.google.maps.android.clustering.ClusterItem
import com.google.maps.android.clustering.algo.NonHierarchicalDistanceBasedAlgorithm
import net.vonforst.evmap.api.goingelectric.ChargeLocation
import net.vonforst.evmap.api.goingelectric.ChargeLocationCluster
import net.vonforst.evmap.api.goingelectric.ChargepointListItem
import net.vonforst.evmap.api.goingelectric.Coordinate
fun cluster(
result: List<ChargepointListItem>,
zoom: Float,
clusterDistance: Int
): List<ChargepointListItem> {
val clusters = result.filterIsInstance<ChargeLocationCluster>()
val locations = result.filterIsInstance<ChargeLocation>()
val clusterItems = locations.map { ChargepointClusterItem(it) }
val algo = NonHierarchicalDistanceBasedAlgorithm<ChargepointClusterItem>()
algo.maxDistanceBetweenClusteredItems = clusterDistance
algo.addItems(clusterItems)
return algo.getClusters(zoom).map {
if (it.size == 1) {
it.items.first().charger
} else {
ChargeLocationCluster(it.size, Coordinate(it.position.latitude, it.position.longitude))
}
} + clusters
}
private class ChargepointClusterItem(val charger: ChargeLocation) : ClusterItem {
override fun getSnippet(): String? = null
override fun getTitle(): String? = charger.name
override fun getPosition(): LatLng = LatLng(charger.coordinates.lat, charger.coordinates.lng)
}
@@ -1,112 +0,0 @@
package net.vonforst.evmap.ui
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.ViewCompat
import androidx.core.widget.NestedScrollView
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.mahc.custombottomsheetbehavior.BottomSheetBehaviorGoogleMapsLike
class HideOnExpandFabBehavior(context: Context, attrs: AttributeSet) :
FloatingActionButton.Behavior(context, attrs) {
override fun onStartNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: FloatingActionButton,
directTargetChild: View,
target: View,
axes: Int,
type: Int
): Boolean {
return axes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(
coordinatorLayout,
child,
directTargetChild,
target,
axes,
type
)
}
override fun layoutDependsOn(
parent: CoordinatorLayout,
child: FloatingActionButton,
dependency: View
): Boolean {
if (dependency is NestedScrollView) {
try {
val behavior = BottomSheetBehaviorGoogleMapsLike.from<View>(dependency)
behavior.addBottomSheetCallback(object :
BottomSheetBehaviorGoogleMapsLike.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
onDependentViewChanged(parent, child, dependency)
}
})
return true
} catch (e: IllegalArgumentException) {
}
}
return false
}
override fun onDependentViewChanged(
parent: CoordinatorLayout,
child: FloatingActionButton,
dependency: View
): Boolean {
val behavior = BottomSheetBehaviorGoogleMapsLike.from<View>(dependency)
when (behavior.state) {
BottomSheetBehaviorGoogleMapsLike.STATE_SETTLING -> {
}
BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN -> {
if (child.tag as? Boolean != false) child.show()
}
BottomSheetBehaviorGoogleMapsLike.STATE_COLLAPSED -> {
if (child.tag as? Boolean != false) child.show()
}
else -> {
child.hide()
}
}
return false
}
override fun onNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: FloatingActionButton,
target: View,
dxConsumed: Int,
dyConsumed: Int,
dxUnconsumed: Int,
dyUnconsumed: Int,
type: Int,
consumed: IntArray
) {
super.onNestedScroll(
coordinatorLayout,
child,
target,
dxConsumed,
dyConsumed,
dxUnconsumed,
dyUnconsumed,
type,
consumed
)
if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {
// User scrolled down and the FAB is currently visible -> hide the FAB
child.hide();
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
// User scrolled up and the FAB is currently not visible -> show the FAB
child.show();
}
}
}
@@ -1,109 +0,0 @@
package net.vonforst.evmap.ui
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.ViewCompat
import androidx.core.widget.NestedScrollView
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.mahc.custombottomsheetbehavior.BottomSheetBehaviorGoogleMapsLike
class HideOnScrollFabBehavior(context: Context, attrs: AttributeSet) :
FloatingActionButton.Behavior(context, attrs) {
override fun onStartNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: FloatingActionButton,
directTargetChild: View,
target: View,
axes: Int,
type: Int
): Boolean {
return axes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(
coordinatorLayout,
child,
directTargetChild,
target,
axes,
type
)
}
override fun layoutDependsOn(
parent: CoordinatorLayout,
child: FloatingActionButton,
dependency: View
): Boolean {
if (dependency is NestedScrollView) {
try {
val behavior = BottomSheetBehaviorGoogleMapsLike.from<View>(dependency)
behavior.addBottomSheetCallback(object :
BottomSheetBehaviorGoogleMapsLike.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
onDependentViewChanged(parent, child, dependency)
}
})
return true
} catch (e: IllegalArgumentException) {
}
}
return false
}
override fun onDependentViewChanged(
parent: CoordinatorLayout,
child: FloatingActionButton,
dependency: View
): Boolean {
val behavior = BottomSheetBehaviorGoogleMapsLike.from<View>(dependency)
when (behavior.state) {
BottomSheetBehaviorGoogleMapsLike.STATE_SETTLING -> {
}
BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN -> {
child.show()
}
else -> {
child.hide()
}
}
return false
}
override fun onNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: FloatingActionButton,
target: View,
dxConsumed: Int,
dyConsumed: Int,
dxUnconsumed: Int,
dyUnconsumed: Int,
type: Int,
consumed: IntArray
) {
super.onNestedScroll(
coordinatorLayout,
child,
target,
dxConsumed,
dyConsumed,
dxUnconsumed,
dyUnconsumed,
type,
consumed
)
if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {
// User scrolled down and the FAB is currently visible -> hide the FAB
child.hide();
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
// User scrolled up and the FAB is currently not visible -> show the FAB
child.show();
}
}
}
@@ -1,162 +0,0 @@
package net.vonforst.evmap.ui
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
import android.util.LruCache
import android.view.ViewGroup
import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.widget.TextViewCompat
import com.car2go.maps.BitmapDescriptorFactory
import com.car2go.maps.model.BitmapDescriptor
import com.google.maps.android.ui.IconGenerator
import com.google.maps.android.ui.SquareTextView
import net.vonforst.evmap.R
class ClusterIconGenerator(context: Context) : IconGenerator(context) {
init {
setBackground(context.getDrawable(R.drawable.marker_cluster_bg))
setContentView(makeSquareTextView(context))
}
private fun makeSquareTextView(context: Context): SquareTextView? {
val density = context.resources.displayMetrics.density
val twelveDpi = (12.0f * density).toInt()
return SquareTextView(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
id = R.id.amu_text
setPadding(twelveDpi, twelveDpi, twelveDpi, twelveDpi)
TextViewCompat.setTextAppearance(this, R.style.TextAppearance_AppCompat)
setTextColor(ContextCompat.getColor(context, android.R.color.white))
}
}
}
class ChargerIconGenerator(val context: Context, val factory: BitmapDescriptorFactory) {
data class BitmapData(
val tint: Int,
val scale: Int,
val alpha: Int,
val highlight: Boolean,
val fault: Boolean
)
val cacheSize = 420; // 420 items: 21 sizes, 5 colors, highlight on/off, fault on/off
val cache = LruCache<BitmapData, BitmapDescriptor>(cacheSize)
val oversize = 1.4f // increase to add padding for fault icon or scale > 1
val icon = R.drawable.ic_map_marker_charging
val highlightIcon = R.drawable.ic_map_marker_highlight
val faultIcon = R.drawable.ic_map_marker_fault
init {
preloadCache()
}
private fun preloadCache() {
// pre-generates images for scale from 0 to 255 for all possible tint colors
val tints = listOf(
R.color.charger_100kw,
R.color.charger_43kw,
R.color.charger_20kw,
R.color.charger_11kw,
R.color.charger_low
)
for (fault in listOf(false, true)) {
for (highlight in listOf(false, true)) {
for (tint in tints) {
for (scale in 0..20) {
getBitmapDescriptor(tint, scale, 255, highlight, fault)
}
}
}
}
}
fun getBitmapDescriptor(
@ColorRes tint: Int,
scale: Int = 20,
alpha: Int = 255,
highlight: Boolean = false,
fault: Boolean = false
): BitmapDescriptor? {
val data = BitmapData(tint, scale, alpha, highlight, fault)
val cachedImg = cache[data]
return if (cachedImg != null) {
cachedImg
} else {
val bitmap = generateBitmap(data)
val bmd = factory.fromBitmap(bitmap)
cache.put(data, bmd)
bmd
}
}
private fun generateBitmap(data: BitmapData): Bitmap {
val vd: Drawable = context.getDrawable(icon)!!
DrawableCompat.setTint(vd, ContextCompat.getColor(context, data.tint));
DrawableCompat.setTintMode(vd, PorterDuff.Mode.MULTIPLY);
val leftPadding = vd.intrinsicWidth * (oversize - 1) / 2
val topPadding = vd.intrinsicWidth * (oversize - 1)
vd.setBounds(
leftPadding.toInt(), topPadding.toInt(),
leftPadding.toInt() + vd.intrinsicWidth,
topPadding.toInt() + vd.intrinsicHeight
)
vd.alpha = data.alpha
val bm = Bitmap.createBitmap(
(vd.intrinsicWidth * oversize).toInt(), (vd.intrinsicHeight * oversize).toInt(),
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bm)
val scale = data.scale / 20f
canvas.scale(
scale,
scale,
leftPadding + vd.intrinsicWidth / 2f,
topPadding + vd.intrinsicHeight.toFloat()
)
vd.draw(canvas)
if (data.highlight) {
val highlightDrawable = context.getDrawable(highlightIcon)!!
highlightDrawable.setBounds(
leftPadding.toInt(), topPadding.toInt(),
leftPadding.toInt() + vd.intrinsicWidth,
topPadding.toInt() + vd.intrinsicHeight
)
highlightDrawable.alpha = data.alpha
highlightDrawable.draw(canvas)
}
if (data.fault) {
val faultDrawable = context.getDrawable(faultIcon)!!
val faultSize = 0.75
val faultShift = 0.25
val base = vd.intrinsicWidth
faultDrawable.setBounds(
(leftPadding.toInt() + base * (1 - faultSize + faultShift)).toInt(),
(topPadding.toInt() - base * faultShift).toInt(),
(leftPadding.toInt() + base * (1 + faultShift)).toInt(),
(topPadding.toInt() + base * (faultSize - faultShift)).toInt()
)
faultDrawable.alpha = data.alpha
faultDrawable.draw(canvas)
}
return bm
}
}
@@ -1,129 +0,0 @@
package net.vonforst.evmap.ui
import android.animation.ValueAnimator
import android.view.animation.BounceInterpolator
import androidx.core.animation.addListener
import androidx.interpolator.view.animation.FastOutLinearInInterpolator
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator
import com.car2go.maps.model.Marker
import net.vonforst.evmap.R
import net.vonforst.evmap.api.goingelectric.ChargeLocation
import kotlin.math.max
fun getMarkerTint(
charger: ChargeLocation,
connectors: Set<String>?
): Int = when {
charger.maxPower(connectors) >= 100 -> R.color.charger_100kw
charger.maxPower(connectors) >= 43 -> R.color.charger_43kw
charger.maxPower(connectors) >= 20 -> R.color.charger_20kw
charger.maxPower(connectors) >= 11 -> R.color.charger_11kw
else -> R.color.charger_low
}
class MarkerAnimator(val gen: ChargerIconGenerator) {
private val animatingMarkers = hashMapOf<Marker, ValueAnimator>()
fun animateMarkerAppear(
marker: Marker,
tint: Int,
highlight: Boolean,
fault: Boolean
) {
animatingMarkers[marker]?.let {
it.cancel()
animatingMarkers.remove(marker)
}
val anim = ValueAnimator.ofInt(0, 20).apply {
duration = 250
interpolator = LinearOutSlowInInterpolator()
addUpdateListener { animationState ->
val scale = animationState.animatedValue as Int
marker.setIcon(
gen.getBitmapDescriptor(
tint,
scale = scale,
highlight = highlight,
fault = fault
)
)
}
addListener(onEnd = {
animatingMarkers.remove(marker)
}, onCancel = {
animatingMarkers.remove(marker)
})
}
animatingMarkers[marker] = anim
anim.start()
}
fun animateMarkerDisappear(
marker: Marker,
tint: Int,
highlight: Boolean,
fault: Boolean
) {
animatingMarkers[marker]?.let {
it.cancel()
animatingMarkers.remove(marker)
}
val anim = ValueAnimator.ofInt(20, 0).apply {
duration = 200
interpolator = FastOutLinearInInterpolator()
addUpdateListener { animationState ->
val scale = animationState.animatedValue as Int
marker.setIcon(
gen.getBitmapDescriptor(
tint,
scale = scale,
highlight = highlight,
fault = fault
)
)
}
addListener(onEnd = {
marker.remove()
animatingMarkers.remove(marker)
}, onCancel = {
marker.remove()
animatingMarkers.remove(marker)
})
}
animatingMarkers[marker] = anim
anim.start()
}
fun deleteMarker(marker: Marker) {
animatingMarkers[marker]?.let {
it.cancel()
animatingMarkers.remove(marker)
}
marker.remove()
}
fun animateMarkerBounce(marker: Marker) {
animatingMarkers[marker]?.let {
it.cancel()
animatingMarkers.remove(marker)
}
val anim = ValueAnimator.ofFloat(0f, 1f).apply {
duration = 700
interpolator = BounceInterpolator()
addUpdateListener { state ->
val t = max(1f - state.animatedValue as Float, 0f) / 2
marker.setAnchor(0.5f, 1.0f + t)
}
addListener(onEnd = {
animatingMarkers.remove(marker)
}, onCancel = {
animatingMarkers.remove(marker)
})
}
animatingMarkers[marker] = anim
anim.start()
}
}
@@ -1,14 +0,0 @@
package net.vonforst.evmap.ui
import androidx.appcompat.app.AppCompatDelegate
import net.vonforst.evmap.storage.PreferenceDataSource
fun updateNightMode(prefs: PreferenceDataSource) {
AppCompatDelegate.setDefaultNightMode(
when (prefs.darkmode) {
"on" -> AppCompatDelegate.MODE_NIGHT_YES
"off" -> AppCompatDelegate.MODE_NIGHT_NO
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
)
}
Loaded 100 of 247 files, more files were not shown because too many files have changed in this diff. Show more