Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Basic NOBIL implementation #363

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open

RFC: Basic NOBIL implementation #363

wants to merge 10 commits into from

Conversation

robho
Copy link
Contributor

@robho robho commented Nov 6, 2024

This adds a NOBIL data source. Data is available in Denmark, Finland, Iceland, Norway and Sweden and the data is collected both from operators via OCPI and contributed by the public.

This is a draft implementation where some functionality is missing. I'm interested in hearing if this is of interest, and if so, what should I fix/work on going forward?

Known problems / missing features:

  • Not all charger data is exposed in the app (location (near restaurant/shopping/...), payment methods, ...)
  • No filters are implemented
  • Chargeprice connection missing
  • Does Android Auto work? Not yet tested
  • When a charging point is removed in nobil it doesn't disappear in the UI (a cache clear is needed). Maybe a generic evmap bug?
  • The code could use some cleanup from someone with Android development experience

@robho robho force-pushed the nobil branch 2 times, most recently from 47f0131 to 80cdd5e Compare November 7, 2024 21:11
@johan12345
Copy link
Collaborator

johan12345 commented Nov 7, 2024

Very nice, thanks a lot! I'll try to find some time in the weekend to try it out and provide some help with code cleanup.

Not all charger data is exposed in the app (photo, location (near restaurant/shopping/...), payment methods, ...)

Yeah, if there are properties that don't fit into EVMap's data structure we can think about adding new fields or expanding existing ones where it is useful. For payment methods, we have the chargecards structure (used by GoingElectric, but a bit more complex than necessary for what NOBIL provides) or we could just put it into the cost.description for now). For location, the most similar field in GoingElectric would be "category", but that is not exposed through their API, so I haven't added it so far. amenities might also work, though that is usually a slightly longer text description in GoingElectric.

No filters are implemented

As previously mentioned, I guess all the filters would have to be implemented locally, as the Nobil API doesn't really have server-side filtering abilities, right? That should be possible (just like the min_connectors filter for GE and OCM), but in the longer term the better solution is probably to first download all chargers into the local DB (which is a capability I'm implementing for #290) and perform filtering there.

Chargeprice connection missing

Chargeprice currently only support GoingElectric and OpenChargeMap unfortunately. And by now they have also created their own separate database of charging stations aggregated from different sources, so from what I heard they are not so interested in fixing issues with their adapters for GE and OCM, let alone adding new ones 🫤.
Also, there is #320 - so I am not sure how long the native Chargeprice integration in EVMap will stay or whether I will have to try to build an alternative at some point.

Does Android Auto work? Not yet tested

Likely yes, I can test that as well.

When a charging point is removed in nobil it doesn't disappear in the UI (a cache clear is needed). Maybe a generic evmap bug?

True, that is probably a general bug - for GoingElectric it's not so relevant as we are only allowed to cache the data for 24h anyway. But yeah, it probably also applies to OpenChargeMap. Let's track that in a separate issue.

referenceData: ReferenceData,
id: Long
): Resource<ChargeLocation> {
// TODO: Nobil ids are "SWE_1234", not Long
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, it seems a bit strange that Nobil has two different types of IDs, the id (integer) and the International_id (string with country prefix and same integer). First I thought that the IDs are assigned separately in each country, so only the International_id is guaranteed to be unique, not the id (and in that case, we would have to use the International_id also for storing in our local database - so the Long field would have to be changed into a String with corresponding DB migration).

On the other hand, according to the API docs, the existingids parameter in API requests expects the "non-international" ID - this would be a bit strange if that ID could appear in multiple countries, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dumped data from Finland, Norway and Sweden and checked the id of all chargers -> the id field is unique across countries. I don't understand the need for two ids either..

I also verified that sending this get-charger-details-request really does require the International_id. No data is returned if the id is sent in the request.

One question I have here is whether this function is used/needed for nobil. All data that nobil returns in responses is "detailed" so is there a need to implement this function?

Thanks for your input and review! I'll go through it all, but could take some time, depending on the time I have to look into it.

// 24: Open 24h
if (chargerStationAttributes.st["24"]?.attrTrans == "Yes") OpeningHours(twentyfourSeven = true, null, null) else null,
// 7: Parking fee
if (chargerStationAttributes.st["7"]?.attrTrans == "No") Cost(freeparking = true) else null,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the attribute is set to "Yes" (i.e. known to have a parking fee), you could also set it to Cost(freeparking = false). It should only be set to null if the parking fee is not known.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other fields from Nobil that might be relevant to the Cost - maybe these could be used to generate a description?

  • Time limit
  • Accessibility (does Open mean freecharging?)
  • Payment Method

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like I either add freecharging and freeparking or descriptions. If I set freecharging and freeparking then EVMap creates a text string for me and ignores the descriptions. Right?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, you can also set both - then freecharging/freeparking will be shown in the first line and the description as a smaller text below.

listOfNotNull(chargerStationData.street, chargerStationData.houseNumber).joinToString(" ")),
chargerStationAttributes.conn.map { createChargepointFromNobilConnection(it.value) },
null,
"",
Copy link
Collaborator

@johan12345 johan12345 Nov 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, Nobil itself doesn't really have a web map intended for the general public, so we can't easily put a link there... Something like https://ladekart.elbil.no/ seems to use different IDs.

For the button at the very bottom ("Source: Nobil") it would probably still be good to have a working link (also to comply with the license terms), but that could just be the home page https://nobil.no. For the share button that of course wouldn't make sense. So maybe we have to add another field called dataSourceUrl (home page of the data source) and make the existing url (direct link to this charger at the data source) optional. Then the "Source" button would use url if available and fall back to dataSourceUrl, while the share button would be disabled if chargerUrl is not available.

Copy link
Contributor Author

@robho robho Nov 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a web page to report errors for Swedish chargers (https://www.energimyndigheten.se/klimat/transporter/laddinfrastruktur/registrera-din-laddstation/elbilsagare/), but for nobil in general they want error reports by email.

Do you think it adds any value to add the web page link for Swedish chargers (I don't think the web page accepts any query parameters so it's not possible to pre-fill charger details)? Can something be done to send an email? Create a mailto link?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we could put this link as the editUrl for chargers in Sweden and use a mailto URL for the other countries. I'm not sure if Android will handle a mailto URL out of the box, but if not, we could parse it in the app and convert it into an email intent.

chargerStationData.zipCode,
listOfNotNull(chargerStationData.street, chargerStationData.houseNumber).joinToString(" ")),
chargerStationAttributes.conn.map { createChargepointFromNobilConnection(it.value) },
null,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nobil seems to have both Operator and Owned_by fields - I think Nobil's Operator is what GoingElectric and EVMap call network and Nobil's Owned_by is what EVMap calls operator. In many cases they are the same (e.g., Ionity chargers are owned and operated by Ionity), but sometimes they are different (e.g., chargers on the streets of Munich are owned and operated by Stadtwerke München, the municipal energy supplier, but are part of the larger Ladenetz network who are responsible for the backend etc.).

The network field is more useful for filters, as there are less networks than operators and pricing usually depends on the network.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, your understanding is correct. Nobil's Operator = EVMap network and Nobil's Owned_by = EVMap operator.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I extracted the network information available in nobil and there are ~1500 different "networks". The field is a text field and users have entered information in many different variations (different capitalization, typos, abbreviations, alternative names, "owned by" information, ...).

Even if I only parse operator from OCPI data I get ~60 "networks" and also here there are name variations for the same actual network and some names don't even correspond to a network.

Maybe we could create a network list manually and try to match charge locations to the list items. Not a great solution since it requires manual work to update the network list and it's hard to know which networks to include in the list, especially in "foreign" countries.

Maybe we have to live without a network filter, for now

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah 😟 Wow, strange that that happens even with OCPI data...
Yeah, then we probably have to live without a network filter for now, and hope that Nobil can improve the data quality in the future.

null,
Instant.now(),
true
)
Copy link
Collaborator

@johan12345 johan12345 Nov 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than what we already have, I think the Availability attribute ( Public/Visitors/Employees/By Appointment/Residents) would be great to add and also have as a filter (at least as a binary filter - only publicly accessible or all)

else -> null
}

return Chargepoint(connectionType, connectionPower, 1, null, null)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

voltage and amperage can also be added (Attributes 12 and 31). It would also be very useful to add a field for the EVSEID (Attribute 28).

app/src/main/java/net/vonforst/evmap/api/nobil/NobilApi.kt Outdated Show resolved Hide resolved
@robho
Copy link
Contributor Author

robho commented Nov 9, 2024

Regarding filtering..

in the longer term the better solution is probably to first download all chargers into the local DB (which is a capability I'm implementing for #290) and perform filtering there.

Is your idea to replace the on-demand-loading with a full data fetch or are you thinking of having some combination of the two?

The nobil data is ~130 Mbytes today (up ~6 Mbytes in ~2 months). This is uncompressed data, I haven't checked if the server can compresss data and what the compressed size is. I think I would prefer to use on-demand loading most of the time, but it would be valuable to have the possibility to cache all data when needed (no mobile connection or high mobile roaming prices, ...).

Is the problem that you see with local filters that they need to fetch data for chargers that are uninteresting? Ie unnecessary network traffic and data processing? Are those "unnecessary" downloads cached even if they are not used?

@johan12345
Copy link
Collaborator

johan12345 commented Nov 12, 2024

The nobil data is ~130 Mbytes today (up ~6 Mbytes in ~2 months). This is uncompressed data, I haven't checked if the server can compresss data and what the compressed size is.

Gzip usually helps quite a lot with JSON data, and the Nobil server does seem to support it. According to

curl --compressed -so /dev/null "https://www.nobil.no/api/server/datadump.php?apikey=<key>&fromdate=2005-01-01&format=json" -w '%{size_download}'

the download size is pretty small at just ~5 MB. Of course it might occupy a bit more space in the local database (and from my experience with the implementation for OSM, parsing the JSON and inserting chargers into the DB also takes much more time than the download itself).

Is your idea to replace the on-demand-loading with a full data fetch or are you thinking of having some combination of the two?

In general, EVMap will continue to support both. So we could offer both options for Nobil. On the other hand, with such a small download size, there's not much of a downside to storing it all locally - might also reduce the load on Nobil's servers in the long term.

Is the problem that you see with local filters that they need to fetch data for chargers that are uninteresting? Ie unnecessary network traffic and data processing?

Yeah. I think the data processing (JSON parsing into Kotlin objects, then filtering) is usually the main bottleneck when loading a large map region with thousands or even ten-thousands of chargers - server-side filtering and even server-side clustering makes it a lot faster for GoingElectric where that is supported.

Also, depending on what filters we implement, there might be some filters where we can only determine the available options by iterating through the whole dataset once (e.g., the network filter mentioned in the comments) and then storing that as ReferenceData.

Are those "unnecessary" downloads cached even if they are not used?

not at the moment, because local filtering happens inside the API implementation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants