Open Charge Map (OCM) aims to document the world's Electric Vehicle (EV) Charging Points. To date, they have produced a dataset of over 200K+ charging point locations around the world. Data is sourced both from volunteers as well as official sources.
The project is led by Christopher Cook, a software developer from Perth, Australia, who has been working on OCM since its inception in 2011.
Below is a heat map of the OCM dataset.
In the EU at least, Charge Point Location density resembles that of the population density map for this region.
When you open the OCM website or app, you'll see a map of charging points.
Each point contains information on the number of station / bays available at the charging location as well as the sockets supported and power delivery. There will be an indication if the station is operational or not, if it's accessible to the public and pricing details.
Users can also submit photographs of the location, its equipment and the surrounding area.
In this blog post, I'll walk through making the OCM dataset analytics-ready and walk through some analysis of the dataset.
My Workstation
I'm using a 6 GHz Intel Core i9-14900K CPU. It has 8 performance cores and 16 efficiency cores with a total of 32 threads and 32 MB of L2 cache. It has a liquid cooler attached and is housed in a spacious, full-sized, Cooler Master HAF 700 computer case. I've come across videos on YouTube where people have managed to overclock the i9-14900KF to 9.1 GHz.
The system has 48 GB of DDR5 RAM clocked at 5,200 MHz and a 5th-generation, Crucial T700 4 TB NVMe M.2 SSD which can read at speeds up to 12,400 MB/s. There is a heatsink on the SSD to help keep its temperature down. This is my system's C drive.
There is also an 8 TB mechanical disk that I use to hold GIS datasets. This is my system's F drive.
The system is powered by a 1,200-watt, fully modular, Corsair Power Supply and is sat on an ASRock Z790 Pro RS Motherboard.
I'm running Ubuntu 22 LTS via Microsoft's Ubuntu for Windows on Windows 11 Pro. In case you're wondering why I don't run a Linux-based desktop as my primary work environment, I'm still using an Nvidia GTX 1080 GPU which has better driver support on Windows and I use ArcGIS Pro from time to time which only supports Windows natively.
Installing Prerequisites
I'll first install some dependencies.
$ sudo apt update
$ sudo apt install \
git \
jq \
libimage-exiftool-perl \
python3-virtualenv
Below I'll set up the Python Virtual Environment.
$ virtualenv ~/.ev
$ source ~/.ev/bin/activate
$ python -m pip install \
rich
Below I'll download and install v0.10.1 of the official binary for DuckDB.
$ cd ~
$ wget -c https://github.com/duckdb/duckdb/releases/download/v0.10.1/duckdb_cli-linux-amd64.zip
$ unzip -j duckdb_cli-linux-amd64.zip
$ chmod +x duckdb
I'll install a few extensions for DuckDB and make sure they load automatically each time I launch the client.
$ ~/duckdb
INSTALL json;
INSTALL parquet;
INSTALL spatial;
$ vi ~/.duckdbrc
.timer on
.width 180
LOAD json;
LOAD parquet;
LOAD spatial;
The maps in this post were rendered with QGIS version 3.36. QGIS is a desktop application that runs on Windows, macOS and Linux. The software is very popular in the geospatial industry and was launched almost 15 million times in May last year.
I used QGIS' Tile+ plugin to add satellite basemaps. Tile+ can be installed from its ZIP distributable.
The steps needed to reproduce the basemap used in this post can be found in my Aircraft Route Analysis post.
Downloading Open Charge Map's Data
The following will clone 204,187 files totalling 307 MB from GitHub. This repository is refreshed monthly.
$ git clone https://github.com/openchargemap/ocm-export ~/open_charge_map
I'll also download a dataset that will help me categorise countries into groups. For instance, Scandinavia, Finland and the Baltics will be classed as 'northernEurope'.
$ git clone https://github.com/vijinho/ISO-Country-Data ~/ISO-Country-Data
Open Charge Map's Data Structure
OCM breaks up data by country and location. Below is an example from a location in Estonia.
$ cd ~/open_charge_map
$ jq -S 'del(.AddressInfo)' data/EE/OCM-11842.json
{
"Connections": [
{
"Amps": 120,
"ConnectionTypeID": 2,
"CurrentTypeID": 30,
"ID": 9252,
"LevelID": 3,
"PowerKW": 50,
"Quantity": 1,
"StatusTypeID": 50,
"Voltage": 500
},
{
"Amps": 32,
"ConnectionTypeID": 25,
"CurrentTypeID": 20,
"ID": 25811,
"LevelID": 2,
"PowerKW": 22,
"Quantity": 1,
"StatusTypeID": 50,
"Voltage": 400
}
],
"DataProviderID": 1,
"DateCreated": "2012-08-03T13:08:00Z",
"DateLastStatusUpdate": "2014-05-29T04:52:00Z",
"DateLastVerified": "2014-05-29T04:52:00Z",
"GeneralComments": "Charger number 37001",
"ID": 11842,
"NumberOfPoints": 1,
"OperatorID": 76,
"StatusTypeID": 50,
"SubmissionStatusTypeID": 200,
"UUID": "810B3C57-F495-4028-B364-937576BD75AB",
"UsageCost": "5β¬/up to 60min",
"UsageTypeID": 1
}
Each file is a location which will be run by a network operator. Each location will have one or more charging stations / bays. A station can have one or more sockets as well as various power delivery specifications. These sockets and power delivery specifications are listed individually in the 'Connections' dictionary and each connection will have a quantity associated with it.
In the above example, only a single vehicle can be charged at any one time at this location and both a 50 kW, DC, 120A, 500V CHAdeMO socket as well as a 22 kW, AC (Three-Phase), 32A, 400V Type 2 socket are supported.
The location files use primary keys for many attributes. Their corresponding records can be found in a referencedata.json file included in the git repository. Below are the 13 reference table names for this project.
$ jq -S '.|keys' referencedata.json
[
"ChargerTypes",
"CheckinStatusTypes",
"ConnectionTypes",
"Countries",
"CurrentTypes",
"DataProviders",
"DataTypes",
"MetadataGroups",
"Operators",
"StatusTypes",
"SubmissionStatusTypes",
"UsageTypes",
"UserCommentTypes"
]
Below are the primary keys and attributes for the charger types.
$ jq -S .ChargerTypes referencedata.json
[
{
"Comments": "Under 2 kW, usually domestic socket types",
"ID": 1,
"IsFastChargeCapable": false,
"Title": "Level 1 : Low (Under 2kW)"
},
{
"Comments": "Over 2 kW, usually non-domestic socket type",
"ID": 2,
"IsFastChargeCapable": false,
"Title": "Level 2 : Medium (Over 2kW)"
},
{
"Comments": "40KW and Higher",
"ID": 3,
"IsFastChargeCapable": true,
"Title": "Level 3: High (Over 40kW)"
}
]
Charge Station Imagery
User-submitted photographs are also listed in this dataset. Within any one location, one or more images may be available. A URL to a thumbnail as well as the original image are listed.
$ jq -S '.MediaItems[0]' data/EE/OCM-103624.json
{
"ChargePointID": 103624,
"Comment": "",
"DateCreated": "2020-07-26T12:19:00Z",
"ID": 23192,
"IsEnabled": true,
"IsExternalResource": false,
"IsFeaturedItem": false,
"IsVideo": false,
"ItemThumbnailURL": "https://media.openchargemap.io/images/EE/OCM103624/OCM-103624.thmb.2020072612183898.jpg",
"ItemURL": "https://media.openchargemap.io/images/EE/OCM103624/OCM-103624.orig.2020072612183898.jpg",
"User": {
"ID": 26470,
"Username": "erkkiat"
}
}
The original images make up ~30 GB altogether. There are a few 20 MB PNGs, especially for Italy and Spain, but for the most part, images are ~2 MB. The vast majority of these photos were taken in Europe with the US and Australia being the only other locations of any significance. Below is the proportion of disk space used by the images broken down by region and country.
The original images don't look to have been through any processing, either to reduce their size or to remove any metadata. Many images contain more than 100 pieces of metadata, such as the image below.
$ wget https://media.openchargemap.io/images/ES/OCM64603/OCM-64603.orig.2016031412054624.jpg
$ exiftool OCM-64603.orig.2016031412054624.jpg | tail -n20
Image Height : 3264
Encoding Process : Baseline DCT, Huffman coding
Bits Per Sample : 8
Color Components : 3
Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2)
Aperture : 2.4
Image Size : 2448x3264
Megapixels : 8.0
Shutter Speed : 1/1374
Create Date : 2016:03:05 11:27:08.053521
Date/Time Original : 2016:03:05 11:27:08.053521
Modify Date : 2016:03:05 11:27:08.053521
Thumbnail Image : (Binary data 5911 bytes, use -b option to extract)
GPS Altitude : 70 m Above Sea Level
GPS Date/Time : 2016:03:05 10:26:59Z
GPS Latitude : 42 deg 10' 36.89" N
GPS Longitude : 3 deg 6' 29.49" E
Focal Length : 4.0 mm
GPS Position : 42 deg 10' 36.89" N, 3 deg 6' 29.49" E
Light Value : 12.9
Some photos are great research material with equipment and signage clearly presented. Others show the surrounding area and landscape which could be a replacement for Google Street View. This is handy as it can show if there is a canopy or if it's in underground / sheltered parking of some sort.
Below is an image found in the Brazilian dataset showing two charging bays in a parking garage.
Surprisingly few people submit photographs of charging point failures. I found this one in the Brazilian dataset.
Instructions to not leave your vehicle while charging in Jordan.
Below is a photo of a canopy covering the charging bays in Malaysia.
Not much space to park in Cyprus.
Below is a photo of a charging point User Interface.
Some people are kind enough to photograph the specifications on the sides of the charging point machines.
Photos often give away what sort of area you'll be waiting around in.
Building an Analysis-Ready Dataset
Below I'll denormalise the OCM dataset by connection. I'll also use the foreign keys to join to the reference datasets. The data will be first exported into JSONL before being imported into DuckDB using its internal file format.
$ python3
from glob import glob
import json
from urllib.parse import urlparse
from rich.progress import track
# Lookups / Reference Data
recs = json.loads(open('referencedata.json', 'r').read())
lookups = {}
for x in recs.keys():
lookups[x] = {}
for rec in recs[x]:
id_ = rec['ID']
del rec['ID']
lookups[x][id_] = rec
# Regions
regions = json.loads(open('/home/mark/ISO-Country-Data/regions.json', 'r').read())
# Connections
station_keys = (
'DateLastVerified',
'ID',
'UUID',
'DataProviderID',
'UsageTypeID',
'UsageCost',
'NumberOfPoints',
'StatusTypeID',
'DateLastStatusUpdate',
'DateCreated',
'SubmissionStatusTypeID',
'ProviderBrand')
with open('connections.json', 'w') as f:
for filename in track(glob('*/OCM-*.json')):
recs = json.loads(open(filename, 'r').read())
if 'RelatedURL' in recs['AddressInfo']:
recs['ProviderBrand'] = \
urlparse(recs['AddressInfo']['RelatedURL'])\
.netloc\
.replace('www.', '')\
.split('.')[0]
country_iso2 = filename.split('/')[0].upper().strip()
region = None
for region_, countries in regions.items():
if country_iso2 in countries:
region = region_
break
if region is None and country_iso2 == 'XK':
region = 'southernEurope'
dp_ = {} if 'DataProviderID' not in recs.keys() else lookups['DataProviders'].get(recs['DataProviderID'], {})
if 'DataProviderStatusType' in dp_.keys():
for k, v in dp_['DataProviderStatusType'].items():
dp_[k] = v
del dp_['DataProviderStatusType']
op_ = {} if 'OperatorID' not in recs.keys() else lookups['Operators'].get(recs['OperatorID'], {})
st_ = {} if 'StatusTypeID' not in recs.keys() else lookups['StatusTypes'].get(recs['StatusTypeID'], {})
us_ = {} if 'UsageTypeID' not in recs.keys() else lookups['UsageTypes'].get(recs['UsageTypeID'], {})
ss_ = {} if 'SubmissionStatusTypeID' not in recs.keys() else lookups['SubmissionStatusTypes'].get(recs['SubmissionStatusTypeID'], {})
for rec in recs['Connections']:
ct_ = {} if 'ConnectionTypeID' not in rec.keys() else lookups['ConnectionTypes'].get(rec['ConnectionTypeID'], {})
cu_ = {} if 'CurrentTypeID' not in rec.keys() else lookups['CurrentTypes'].get(rec['CurrentTypeID'], {})
cs_ = {} if 'StatusTypeID' not in rec.keys() else lookups['StatusTypes'].get(rec['StatusTypeID'], {})
f.write(json.dumps({**{'AddressCountryIso2': country_iso2,
'AddressWorldRegion': region},
**{'Connector' + k: v for k, v in rec.items()},
**{'Address' + k: v for k, v in recs['AddressInfo'].items()},
**{'DataProvider' + k: v for k, v in dp_.items()},
**{'Operator' + k: v for k, v in op_.items()},
**{'StatusType' + k: v for k, v in st_.items()},
**{'UsageType' + k: v for k, v in us_.items()},
**{'SubmissionStatusType' + k: v for k, v in ss_.items()},
**{'ConnectionType' + k: v for k, v in ct_.items()},
**{'CurrentType' + k: v for k, v in cu_.items()},
**{'ConnectionStatusType' + k: v for k, v in cs_.items()},
**{'Location' + k: recs.get(k, None)
for k in station_keys}}) + '\n')
The following will convert the JSONL into a DuckDB table using its internal file format.
$ ~/duckdb -c "CREATE OR REPLACE TABLE connections aS
SELECT *
FROM 'connections.json';" \
connections.duckdb
The resulting DuckDB file is 98 MB and consists of 373,428 records. Below is an example record.
$ ~/duckdb -json \
-c "SELECT *
FROM connections
WHERE AddressCountryIso2 = 'EE'
LIMIT 1;" \
connections.duckdb \
| tail -n1 \
| jq -S .
[
{
"AddressAccessComments": null,
"AddressAddressLine1": "Mere Boulevard 7",
"AddressAddressLine2": null,
"AddressContactEmail": null,
"AddressContactTelephone1": "+372 4473888",
"AddressContactTelephone2": null,
"AddressCountryID": 74,
"AddressCountryIso2": "EE",
"AddressDistanceUnit": 0,
"AddressID": 103970,
"AddressLatitude": 58.380532,
"AddressLongitude": 24.491924,
"AddressPostcode": "80010",
"AddressRelatedURL": null,
"AddressStateOrProvince": null,
"AddressTitle": "Villa Ammende hotel and restaurant",
"AddressTown": "PΓ€rnu",
"AddressWorldRegion": "northernEurope",
"ConnectionStatusTypeIsOperational": true,
"ConnectionStatusTypeIsUserSelectable": true,
"ConnectionStatusTypeTitle": "Operational",
"ConnectionTypeFormalName": null,
"ConnectionTypeIsDiscontinued": false,
"ConnectionTypeIsObsolete": false,
"ConnectionTypeTitle": "Tesla (Model S/X)",
"ConnectorAmps": 32,
"ConnectorComments": null,
"ConnectorConnectionTypeID": 30,
"ConnectorCurrentTypeID": 20,
"ConnectorID": 146294,
"ConnectorLevelID": 2,
"ConnectorPowerKW": 22,
"ConnectorQuantity": 1,
"ConnectorReference": null,
"ConnectorStatusTypeID": 50,
"ConnectorVoltage": 400,
"CurrentTypeDescription": "Alternating Current - Three Phase",
"CurrentTypeTitle": "AC (Three-Phase)",
"DataProviderComments": null,
"DataProviderDateLastImported": null,
"DataProviderID": 1,
"DataProviderIsApprovedImport": true,
"DataProviderIsOpenDataLicensed": true,
"DataProviderIsProviderEnabled": true,
"DataProviderIsRestrictedEdit": false,
"DataProviderLicense": "Licensed under Creative Commons Attribution 4.0 International (CC BY 4.0)",
"DataProviderTitle": "Manual Data Entry",
"DataProviderWebsiteURL": "http://openchargemap.org",
"LocationDataProviderID": 1,
"LocationDateCreated": "2018-05-05 05:44:00",
"LocationDateLastStatusUpdate": "2020-07-27 14:52:00",
"LocationDateLastVerified": "2020-07-27 14:52:00",
"LocationID": 103624,
"LocationNumberOfPoints": 2,
"LocationProviderBrand": null,
"LocationStatusTypeID": 50,
"LocationSubmissionStatusTypeID": 200,
"LocationUUID": "56ae3b26-c6c0-4770-98bb-aa2beb850147",
"LocationUsageCost": null,
"LocationUsageTypeID": 4,
"OperatorAddressInfo": null,
"OperatorBookingURL": null,
"OperatorComments": null,
"OperatorContactEmail": null,
"OperatorFaultReportEmail": null,
"OperatorIsPrivateIndividual": false,
"OperatorIsRestrictedEdit": false,
"OperatorPhonePrimaryContact": null,
"OperatorPhoneSecondaryContact": null,
"OperatorTitle": "Tesla (Tesla-only charging)",
"OperatorWebsiteURL": "http://www.teslamotors.com",
"StatusTypeIsOperational": true,
"StatusTypeIsUserSelectable": true,
"StatusTypeTitle": "Operational",
"SubmissionStatusTypeIsLive": true,
"SubmissionStatusTypeTitle": "Submission Published",
"UsageTypeIsAccessKeyRequired": true,
"UsageTypeIsMembershipRequired": true,
"UsageTypeIsPayAtLocation": false,
"UsageTypeTitle": "Public - Membership Required"
}
]
Charging Points Around The World
I'll break down the number of charging locations by region. These will be broken up into categories of how many stations / bays are available at each location. So far, there is only one location in the world, located in Sweden, with more than 500 bays.
$ ~/duckdb connections.duckdb
SELECT AddressWorldRegion,
COUNT(DISTINCT LocationUUID) FILTER (WHERE LocationNumberOfPoints == 1) AS "1",
COUNT(DISTINCT LocationUUID) FILTER (WHERE LocationNumberOfPoints >= 2 AND LocationNumberOfPoints < 5) AS "2_4",
COUNT(DISTINCT LocationUUID) FILTER (WHERE LocationNumberOfPoints >= 5 AND LocationNumberOfPoints < 20) AS "5_19",
COUNT(DISTINCT LocationUUID) FILTER (WHERE LocationNumberOfPoints >= 20 AND LocationNumberOfPoints < 50) AS "20_49",
COUNT(DISTINCT LocationUUID) FILTER (WHERE LocationNumberOfPoints >= 50 AND LocationNumberOfPoints < 500) AS "50_499",
COUNT(DISTINCT LocationUUID) FILTER (WHERE LocationNumberOfPoints >= 500) AS "500_plus",
FROM connections
GROUP BY AddressWorldRegion
ORDER BY 2 DESC;
ββββββββββββββββββββββ¬ββββββββ¬ββββββββ¬ββββββββ¬ββββββββ¬βββββββββ¬βββββββββββ
β AddressWorldRegion β 1 β 2_4 β 5_19 β 20_49 β 50_499 β 500_plus β
β varchar β int64 β int64 β int64 β int64 β int64 β int64 β
ββββββββββββββββββββββΌββββββββΌββββββββΌββββββββΌββββββββΌβββββββββΌβββββββββββ€
β westernEurope β 11561 β 13919 β 1561 β 122 β 10 β 0 β
β northernEurope β 7861 β 16216 β 5126 β 418 β 98 β 1 β
β southernEurope β 6676 β 17910 β 1607 β 80 β 10 β 0 β
β northernAmerica β 3634 β 5589 β 2458 β 193 β 13 β 0 β
β easternEurope β 2630 β 1590 β 68 β 1 β 0 β 0 β
β australia β 906 β 989 β 136 β 1 β 0 β 0 β
β westernAsia β 774 β 360 β 37 β 1 β 0 β 0 β
β eastAsia β 446 β 148 β 25 β 18 β 0 β 0 β
β caribbean β 341 β 39 β 0 β 0 β 0 β 0 β
β southAmerica β 267 β 95 β 7 β 2 β 0 β 0 β
β southeastAsia β 247 β 162 β 12 β 1 β 0 β 0 β
β polynesia β 156 β 7 β 1 β 0 β 0 β 0 β
β centralAmerica β 115 β 45 β 1 β 0 β 0 β 0 β
β northAfrica β 98 β 49 β 3 β 0 β 0 β 0 β
β southernAsia β 91 β 30 β 1 β 0 β 0 β 0 β
β southernAfrica β 35 β 107 β 0 β 0 β 0 β 0 β
β eastAfrica β 15 β 7 β 2 β 0 β 0 β 0 β
β westAfrica β 2 β 0 β 1 β 0 β 0 β 0 β
β centralAsia β 2 β 1 β 1 β 0 β 0 β 0 β
ββββββββββββββββββββββ΄ββββββββ΄ββββββββ΄ββββββββ΄ββββββββ΄βββββββββ΄βββββββββββ€
β 19 rows 7 columns β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
This is a breakdown for countries outside of Europe, North America and Australia. Some of these countries are fairly small and have very good charging point coverage but others, I suspect, are vastly underrepresented in this dataset, like China with 126 locations listed.
SELECT AddressCountryIso2,
COUNT(DISTINCT LocationUUID) FILTER (WHERE LocationNumberOfPoints == 1) AS "1",
COUNT(DISTINCT LocationUUID) FILTER (WHERE LocationNumberOfPoints >= 2 AND LocationNumberOfPoints < 5) AS "2_4",
COUNT(DISTINCT LocationUUID) FILTER (WHERE LocationNumberOfPoints >= 5 AND LocationNumberOfPoints < 20) AS "5_19",
COUNT(DISTINCT LocationUUID) FILTER (WHERE LocationNumberOfPoints >= 20 AND LocationNumberOfPoints < 50) AS "20_49",
COUNT(DISTINCT LocationUUID) FILTER (WHERE LocationNumberOfPoints >= 50 AND LocationNumberOfPoints < 500) AS "50_499",
COUNT(DISTINCT LocationUUID) FILTER (WHERE LocationNumberOfPoints >= 500) AS "500_plus",
FROM connections
WHERE AddressWorldRegion NOT LIKE '%Europe'
AND AddressWorldRegion != 'northernAmerica'
AND AddressCountryIso2 != 'AU'
GROUP BY AddressCountryIso2
ORDER BY 2 DESC
LIMIT 20;
ββββββββββββββββββββββ¬ββββββββ¬ββββββββ¬ββββββββ¬ββββββββ¬βββββββββ¬βββββββββββ
β AddressCountryIso2 β 1 β 2_4 β 5_19 β 20_49 β 50_499 β 500_plus β
β varchar β int64 β int64 β int64 β int64 β int64 β int64 β
ββββββββββββββββββββββΌββββββββΌββββββββΌββββββββΌββββββββΌβββββββββΌβββββββββββ€
β TR β 522 β 177 β 13 β 1 β 0 β 0 β
β NZ β 389 β 510 β 72 β 1 β 0 β 0 β
β DO β 331 β 6 β 0 β 0 β 0 β 0 β
β JP β 226 β 13 β 1 β 0 β 0 β 0 β
β BR β 206 β 51 β 3 β 1 β 0 β 0 β
β IL β 166 β 81 β 20 β 0 β 0 β 0 β
β CL β 156 β 7 β 1 β 0 β 0 β 0 β
β CN β 121 β 4 β 1 β 0 β 0 β 0 β
β CR β 111 β 45 β 1 β 0 β 0 β 0 β
β ID β 110 β 78 β 1 β 0 β 0 β 0 β
β MY β 95 β 78 β 10 β 1 β 0 β 0 β
β MA β 71 β 29 β 2 β 0 β 0 β 0 β
β HK β 67 β 117 β 20 β 18 β 0 β 0 β
β IN β 48 β 13 β 0 β 0 β 0 β 0 β
β AE β 48 β 65 β 4 β 0 β 0 β 0 β
β LK β 40 β 16 β 1 β 0 β 0 β 0 β
β ZA β 34 β 106 β 0 β 0 β 0 β 0 β
β JO β 34 β 34 β 0 β 0 β 0 β 0 β
β TW β 32 β 12 β 3 β 0 β 0 β 0 β
β UY β 25 β 9 β 1 β 0 β 0 β 0 β
ββββββββββββββββββββββ΄ββββββββ΄ββββββββ΄ββββββββ΄ββββββββ΄βββββββββ΄βββββββββββ€
β 20 rows 7 columns β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Much of the data has been submitted by volunteers but there are also official sources as well. Below are the most significant official sources broken down by region.
SELECT AddressWorldRegion,
REPLACE(SPLIT_PART(REPLACE(REPLACE(DataProviderWebsiteURL, 'http://', ''), 'https://', ''), '/', 1), 'www.', '') AS domain,
COUNT(DISTINCT LocationUUID) n_locations
FROM connections
WHERE DataProviderWebsiteURL != 'http://openchargemap.org'
GROUP BY 1, 2
HAVING n_locations > 50
ORDER BY 1, 3 DESC;
ββββββββββββββββββββββ¬ββββββββββββββββββββββββββ¬ββββββββββββββ
β AddressWorldRegion β domain β n_locations β
β varchar β varchar β int64 β
ββββββββββββββββββββββΌββββββββββββββββββββββββββΌββββββββββββββ€
β eastAsia β chademo.com β 1268 β
β eastAsia β oplaadpalen.nl β 182 β
β eastAsia β carstations.com β 117 β
β easternEurope β sitronics-electro.com β 93 β
β northernAmerica β afdc.energy.gov β 57074 β
β northernAmerica β carstations.com β 2464 β
β northernEurope β chargepoints.dft.gov.uk β 11864 β
β northernEurope β nobil.no β 9081 β
β northernEurope β ev.lakd.lt β 234 β
β southernEurope β mobie.pt β 751 β
β southernEurope β oplaadpalen.nl β 59 β
β westernEurope β bundesnetzagentur.de β 10533 β
β westernEurope β data.gouv.fr β 10230 β
β westernEurope β oplaadpalen.nl β 7698 β
ββββββββββββββββββββββ΄ββββββββββββββββββββββββββ΄ββββββββββββββ€
β 14 rows 3 columns β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
There has been a move to either partly or fully automate data imports. Below is a breakdown by each location's last update as to which process was used to refresh its record.
SELECT SUBSTR(LocationDateLastStatusUpdate::TEXT, 1, 4) last_update,
COUNT(DISTINCT LocationUUID) FILTER (WHERE DataProviderTitle = 'Manual Data Entry') as manual,
COUNT(DISTINCT LocationUUID) FILTER (WHERE DataProviderTitle = 'Partially Automated (Ad-Hoc Import)') as partly,
COUNT(DISTINCT LocationUUID) FILTER (WHERE DataProviderTitle = 'Automated Import') as automated
FROM connections
GROUP BY last_update
ORDER BY last_update DESC;
βββββββββββββββ¬βββββββββ¬βββββββββ¬ββββββββββββ
β last_update β manual β partly β automated β
β varchar β int64 β int64 β int64 β
βββββββββββββββΌβββββββββΌβββββββββΌββββββββββββ€
β 2024 β 5362 β 6288 β 42766 β
β 2023 β 25136 β 2764 β 17231 β
β 2022 β 11433 β 0 β 5729 β
β 2021 β 5590 β 253 β 10592 β
β 2020 β 9507 β 7 β 9275 β
β 2019 β 7478 β 127 β 4861 β
β 2018 β 8151 β 1 β 25 β
β 2017 β 8546 β 1 β 2484 β
β 2016 β 4568 β 18 β 7857 β
β 2015 β 3902 β 0 β 105 β
β 2014 β 650 β 0 β 120 β
β 2013 β 523 β 0 β 3 β
β 2012 β 182 β 1 β 0 β
β 2011 β 89 β 0 β 0 β
β 2010 β 25 β 0 β 0 β
βββββββββββββββ΄βββββββββ΄βββββββββ΄ββββββββββββ€
β 15 rows 4 columns β
βββββββββββββββββββββββββββββββββββββββββββββ
There has been a considerable amount of effort put into refreshing data. Below is a breakdown of the number of locations refreshed before and after 2021.
SELECT AddressWorldRegion,
COUNT(DISTINCT LocationUUID) FILTER (WHERE SUBSTR(LocationDateLastVerified::VARCHAR, 1, 4)::INT < 2021) AS "before_2021",
COUNT(DISTINCT LocationUUID) FILTER (WHERE SUBSTR(LocationDateLastVerified::VARCHAR, 1, 4)::INT >= 2021) AS "2021_onward"
FROM connections
GROUP BY AddressWorldRegion
ORDER BY 2 DESC;
ββββββββββββββββββββββ¬ββββββββββββββ¬ββββββββββββββ
β AddressWorldRegion β before_2021 β 2021_onward β
β varchar β int64 β int64 β
ββββββββββββββββββββββΌββββββββββββββΌββββββββββββββ€
β westernEurope β 28264 β 19871 β
β northernAmerica β 19253 β 53468 β
β northernEurope β 10155 β 32841 β
β southernEurope β 7443 β 19981 β
β eastAsia β 1860 β 46 β
β easternEurope β 1758 β 2650 β
β australia β 480 β 1560 β
β westernAsia β 388 β 943 β
β centralAmerica β 52 β 109 β
β southeastAsia β 37 β 385 β
β southernAsia β 21 β 101 β
β southernAfrica β 16 β 126 β
β southAmerica β 15 β 357 β
β northAfrica β 13 β 137 β
β caribbean β 9 β 371 β
β polynesia β 5 β 172 β
β eastAfrica β 3 β 21 β
β centralAsia β 2 β 2 β
β westAfrica β 0 β 3 β
ββββββββββββββββββββββ΄ββββββββββββββ΄ββββββββββββββ€
β 19 rows 3 columns β
ββββββββββββββββββββββββββββββββββββββββββββββββββ
Charging Point Operators
ChargePoint, Tesla, BlinkCharging, flo and Shell Recharge Solutions are the most significant operators with more than 3K locations and a significant presence outside of their home market.
CREATE OR REPLACE TEMPORARY TABLE op_locations AS
SELECT SPLIT_PART(OperatorTitle, ' (', 1) AS OperatorTitle,
AddressCountryIso2,
COUNT(DISTINCT LocationUUID) n_locations
FROM connections
WHERE LENGTH(OperatorTitle) > 1
AND OperatorTitle NOT LIKE '(Unknown Operator)'
AND OperatorTitle NOT LIKE '(Business Owner at Location)'
GROUP BY 1, 2
HAVING n_locations > 1000;
CREATE OR REPLACE TEMPORARY TABLE op_locations_fillin AS
SELECT SPLIT_PART(OperatorTitle, ' (', 1) AS OperatorTitle,
AddressCountryIso2,
COUNT(DISTINCT LocationUUID) n_locations
FROM connections
WHERE SPLIT_PART(OperatorTitle, ' (', 1) IN (
SELECT DISTINCT OperatorTitle
FROM op_locations)
AND AddressCountryIso2 IN (
SELECT DISTINCT AddressCountryIso2
FROM op_locations)
GROUP BY 1, 2;
WITH pivot_alias AS (
PIVOT op_locations_fillin
ON AddressCountryIso2
USING SUM(n_locations)
GROUP BY OperatorTitle
)
SELECT OperatorTitle,
US,
CA,
* EXCLUDE(OperatorTitle, US, CA)
FROM pivot_alias
ORDER BY US DESC;
ββββββββββββββββββββββββββββ¬βββββββββ¬βββββββββ¬βββββββββ¬βββββββββ¬βββββββββ¬βββββββββ¬βββββββββ¬βββββββββ¬βββββββββ
β OperatorTitle β US β CA β DE β ES β FR β GB β IT β NL β PT β
β varchar β int128 β int128 β int128 β int128 β int128 β int128 β int128 β int128 β int128 β
ββββββββββββββββββββββββββββΌβββββββββΌβββββββββΌβββββββββΌβββββββββΌβββββββββΌβββββββββΌβββββββββΌβββββββββΌβββββββββ€
β ChargePoint β 23374 β 2483 β 19 β β 53 β 317 β 14 β 20 β β
β Tesla β 7242 β 825 β 790 β 83 β 532 β 631 β 711 β 128 β 178 β
β Blink Charging β 6432 β 27 β β β β 552 β β β β
β EV Connect β 1687 β 64 β β β β β β β β
β Shell Recharge Solutions β 1289 β 95 β 823 β β 4 β 273 β 1 β 1228 β β
β eVgo Network β 1220 β β β β β β β β β
β PEA Volta β 1191 β 1 β β β β β β β β
β flo β 783 β 2958 β β β β β β β β
β Enel X β 16 β β β 1 β β 2 β 4124 β β β
β EV-Box β 3 β β 9 β β 3 β 17 β 24 β 2055 β β
β Endesa β β β β 1291 β β β β β β
β Izivia β β β β β 1406 β β β β β
β Mobie.pt β β β β β β β β β 1769 β
β EVnetNL β β β 6 β β β β β 1788 β β
β ubitricity β β β 57 β β 3 β 5253 β β β β
β POD Point β β β β β β 3342 β β 1 β β
β Chargeplace Scotland β β β β β β 1359 β β β β
β Innogy SE β β β 2205 β β β 1 β β β β
β Circuit Electrique β β 2089 β β β β β β β β
β EnBW β β β 1268 β β β β β β β
β Char.gy β β β β β β 2862 β β β β
β Source London β β β β β β 1172 β β β β
β Mercadona β β β β 1071 β β β β β β
β BP Pulse β β β β 73 β β 2157 β β β β
β SureCharge β β β β β β 1780 β β β β
β Iberdrola β β β β 1734 β β β β β 36 β
β ladenetz.de β β β 1488 β β β β β 2 β β
ββββββββββββββββββββββββββββ΄βββββββββ΄βββββββββ΄βββββββββ΄βββββββββ΄βββββββββ΄βββββββββ΄βββββββββ΄βββββββββ΄βββββββββ€
β 27 rows 10 columns β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Charging Infrastructure
Below are the most popular connection types in Europe.
SELECT ConnectionTypeTitle,
COUNT(*)
FROM connections
WHERE AddressWorldRegion LIKE '%Europe'
GROUP BY 1
ORDER BY 2 DESC
LIMIT 10;
βββββββββββββββββββββββββββββββββββββ¬βββββββββββββββ
β ConnectionTypeTitle β count_star() β
β varchar β int64 β
βββββββββββββββββββββββββββββββββββββΌβββββββββββββββ€
β Type 2 (Socket Only) β 147088 β
β CCS (Type 2) β 32961 β
β CEE 7/4 - Schuko - Type F β 23758 β
β Type 2 (Tethered Connector) β 22946 β
β CHAdeMO β 21898 β
β Unknown β 12391 β
β Tesla (Model S/X) β 4749 β
β SCAME Type 3A (Low Power) β 2257 β
β SCAME Type 3C (Schneider-Legrand) β 2159 β
β Type 1 (J1772) β 1445 β
βββββββββββββββββββββββββββββββββββββ΄βββββββββββββββ€
β 10 rows 2 columns β
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
Below are the most popular connection types in North America.
SELECT ConnectionTypeTitle,
COUNT(*)
FROM connections
WHERE AddressWorldRegion = 'northernAmerica'
GROUP BY 1
ORDER BY 2 DESC
LIMIT 10;
βββββββββββββββββββββββββββββ¬βββββββββββββββ
β ConnectionTypeTitle β count_star() β
β varchar β int64 β
βββββββββββββββββββββββββββββΌβββββββββββββββ€
β Type 1 (J1772) β 56622 β
β CCS (Type 1) β 9869 β
β CHAdeMO β 7554 β
β Tesla (Model S/X) β 6356 β
β NACS / Tesla Supercharger β 2663 β
β NEMA 5-20R β 1375 β
β Unknown β 443 β
β NEMA 5-15R β 247 β
β NEMA 14-50 β 231 β
β Avcon Connector β 44 β
βββββββββββββββββββββββββββββ΄βββββββββββββββ€
β 10 rows 2 columns β
ββββββββββββββββββββββββββββββββββββββββββββ
There is a wide variety of power delivery specifications for each connection type. These are the most popular in Europe.
SELECT ConnectionTypeTitle AS Connector,
ConnectorVoltage AS Voltage,
ConnectorAmps AS Amps,
ConnectorPowerKW AS PowerKW,
CurrentTypeTitle AS Current,
COUNT(DISTINCT LocationUUID) AS n_locations
FROM connections
WHERE AddressWorldRegion LIKE '%Europe'
AND ConnectorVoltage > 0
GROUP BY 1, 2, 3, 4, 5
ORDER BY 6 DESC
LIMIT 20;
ββββββββββββββββββββββββββββββββ¬ββββββββββ¬ββββββββ¬ββββββββββ¬ββββββββββββββββββββ¬ββββββββββββββ
β Connector β Voltage β Amps β PowerKW β Current β n_locations β
β varchar β int64 β int64 β double β varchar β int64 β
ββββββββββββββββββββββββββββββββΌββββββββββΌββββββββΌββββββββββΌββββββββββββββββββββΌββββββββββββββ€
β Type 2 (Socket Only) β 400 β 32 β 22.0 β AC (Three-Phase) β 22279 β
β Type 2 (Socket Only) β 230 β 32 β 7.0 β AC (Single-Phase) β 7525 β
β Type 2 (Socket Only) β 230 β 16 β 3.7 β AC (Single-Phase) β 5311 β
β Type 2 (Socket Only) β 400 β 16 β 11.0 β AC (Three-Phase) β 4032 β
β CEE 7/4 - Schuko - Type F β 230 β 16 β 3.7 β AC (Single-Phase) β 3202 β
β Type 2 (Tethered Connector) β 400 β 63 β 43.0 β AC (Three-Phase) β 2089 β
β CHAdeMO β 400 β 125 β 50.0 β DC β 1819 β
β Tesla (Model S/X) β 400 β 32 β 22.0 β AC (Three-Phase) β 1811 β
β Type 2 (Socket Only) β 220 β 7 β 4.8 β AC (Single-Phase) β 1797 β
β Type 2 (Socket Only) β 230 β 22 β 5.1 β AC (Single-Phase) β 1771 β
β Type 2 (Tethered Connector) β 400 β 32 β 22.0 β AC (Three-Phase) β 1712 β
β CHAdeMO β 500 β 100 β 50.0 β DC β 1613 β
β Type 2 (Socket Only) β 230 β 7 β 5.0 β AC (Single-Phase) β 1582 β
β SCAME Type 3A (Low Power) β 240 β 16 β 3.7 β AC (Single-Phase) β 1495 β
β Type 2 (Socket Only) β 400 β 32 β 22.0 β DC β 1450 β
β Unknown β 230 β 16 β 3.7 β β 1414 β
β CCS (Type 2) β 500 β 100 β 50.0 β DC β 1051 β
β Type 2 (Socket Only) β 230 β 32 β 7.4 β AC (Single-Phase) β 1036 β
β CHAdeMO β 500 β 120 β 50.0 β DC β 1016 β
β CCS (Type 2) β 400 β 125 β 50.0 β DC β 1003 β
ββββββββββββββββββββββββββββββββ΄ββββββββββ΄ββββββββ΄ββββββββββ΄ββββββββββββββββββββ΄ββββββββββββββ€
β 20 rows 6 columns β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
These are the most popular in North America.
SELECT ConnectionTypeTitle,
ConnectorVoltage,
ConnectorAmps,
ConnectorPowerKW,
CurrentTypeTitle,
COUNT(DISTINCT LocationUUID) n_locations
FROM connections
WHERE AddressWorldRegion = 'northernAmerica'
AND ConnectorVoltage > 0
GROUP BY 1, 2, 3, 4, 5
ORDER BY 6 DESC
LIMIT 20;
βββββββββββββββββββββββ¬βββββββββββββββββββ¬ββββββββββββββββ¬βββββββββββββββββββ¬ββββββββββββββββββββ¬ββββββββββββββ
β ConnectionTypeTitle β ConnectorVoltage β ConnectorAmps β ConnectorPowerKW β CurrentTypeTitle β n_locations β
β varchar β int64 β int64 β double β varchar β int64 β
βββββββββββββββββββββββΌβββββββββββββββββββΌββββββββββββββββΌβββββββββββββββββββΌββββββββββββββββββββΌββββββββββββββ€
β Type 1 (J1772) β 230 β 16 β 3.7 β AC (Single-Phase) β 45891 β
β Type 1 (J1772) β 230 β 16 β 3.0 β AC (Single-Phase) β 4842 β
β Tesla (Model S/X) β 230 β 16 β 3.7 β AC (Single-Phase) β 1419 β
β CCS (Type 1) β 400 β 100 β 40.0 β DC β 1338 β
β CHAdeMO β 400 β 100 β 40.0 β DC β 1180 β
β Type 1 (J1772) β 230 β 32 β 7.0 β AC (Single-Phase) β 698 β
β Tesla (Model S/X) β 230 β 16 β 16.0 β AC (Single-Phase) β 568 β
β Tesla (Model S/X) β 230 β 16 β 8.0 β AC (Single-Phase) β 433 β
β NEMA 5-20R β 120 β 16 β 1.9 β AC (Single-Phase) β 414 β
β NEMA 5-20R β 120 β 16 β 1.0 β AC (Single-Phase) β 233 β
β NEMA 5-15R β 120 β 16 β 1.9 β AC (Single-Phase) β 222 β
β Tesla (Model S/X) β 230 β 16 β 13.0 β AC (Single-Phase) β 165 β
β CCS (Type 1) β 400 β 100 β 40.0 β AC (Single-Phase) β 123 β
β NEMA 14-50 β 120 β 16 β 1.9 β AC (Single-Phase) β 123 β
β Unknown β 230 β 16 β 3.0 β AC (Single-Phase) β 113 β
β CHAdeMO β 400 β 100 β 40.0 β AC (Single-Phase) β 111 β
β Type 1 (J1772) β 240 β 72 β 17.3 β β 110 β
β CCS (Type 1) β 400 β 125 β 50.0 β DC β 92 β
β CHAdeMO β 400 β 125 β 50.0 β DC β 92 β
β Unknown β 120 β 16 β 1.9 β AC (Single-Phase) β 50 β
βββββββββββββββββββββββ΄βββββββββββββββββββ΄ββββββββββββββββ΄βββββββββββββββββββ΄ββββββββββββββββββββ΄ββββββββββββββ€
β 20 rows 6 columns β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Looking at the most popular connection and power delivery combination for North America, it's not one but many operators that offer this combination.
SELECT OperatorTitle,
ConnectionTypeTitle,
COUNT(DISTINCT LocationUUID) n_locations
FROM connections
WHERE ConnectorVoltage = 230
AND ConnectorAmps = 16
AND ConnectorPowerKW = 3.7
AND CurrentTypeTitle = 'AC (Single-Phase)'
AND LENGTH(OperatorTitle) > 1
AND AddressWorldRegion = 'northernAmerica'
GROUP BY 1, 2
ORDER BY 3 DESC
LIMIT 20;
βββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββ¬ββββββββββββββ
β OperatorTitle β ConnectionTypeTitle β n_locations β
β varchar β varchar β int64 β
βββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββΌββββββββββββββ€
β ChargePoint β Type 1 (J1772) β 18837 β
β Blink Charging β Type 1 (J1772) β 5770 β
β flo β Type 1 (J1772) β 2654 β
β Circuit Electrique β Type 1 (J1772) β 1718 β
β EV Connect β Type 1 (J1772) β 1193 β
β Tesla (Tesla-only charging) β Tesla (Model S/X) β 1184 β
β PEA Volta β Type 1 (J1772) β 1141 β
β Shell Recharge Solutions (US) β Type 1 (J1772) β 882 β
β AmpUp β Type 1 (J1772) β 764 β
β SWTCH - Charge Everywhere β Type 1 (J1772) β 482 β
β SemaConnect β Type 1 (J1772) β 394 β
β Livingston Charge Port β Type 1 (J1772) β 325 β
β OpConnect β Type 1 (J1772) β 296 β
β eVgo Network β Type 1 (J1772) β 230 β
β GE WattStation (No longer active) β Type 1 (J1772) β 162 β
β Rivian Waypoints L2 Network β Type 1 (J1772) β 160 β
β ChargeUp (NovaCharge) β Type 1 (J1772) β 146 β
β Universal EV Charging β Type 1 (J1772) β 143 β
β Blink Charging (Europe) β Type 1 (J1772) β 136 β
β Powerflex β Type 1 (J1772) β 134 β
βββββββββββββββββββββββββββββββββββββ΄ββββββββββββββββββββββ΄ββββββββββββββ€
β 20 rows 3 columns β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Charging Point Access
In terms of location access, the vast majority are publically available. Of those that are private, they still might be accessible as both "For Staff, Visitors or Customers" and "Privately Owned - Notice Required" are included in the 16,229 locations below.
SELECT COUNT(DISTINCT LocationUUID) FILTER (WHERE UsageTypeTitle LIKE 'Public%') AS public,
COUNT(DISTINCT LocationUUID) FILTER (WHERE UsageTypeTitle LIKE 'Private%') AS private,
COUNT(DISTINCT LocationUUID) FILTER (WHERE UsageTypeTitle NOT LIKE 'Public%'
AND UsageTypeTitle NOT LIKE 'Private%') AS unknown,
FROM connections
ORDER BY 1 DESC;
ββββββββββ¬ββββββββββ¬ββββββββββ
β public β private β unknown β
β int64 β int64 β int64 β
ββββββββββΌββββββββββΌββββββββββ€
β 150481 β 16229 β 657 β
ββββββββββ΄ββββββββββ΄ββββββββββ