Explorando os dados públicos de Curitiba com Python 💻📍🏠

Caroline Attilio
6 min readMar 9, 2021

--

Uma divertida análise de dados sobre a linha de turismo da cidade de Curitiba usando API do Google Maps, folium, polyline e geopandas 🍃

Imagem: https://unsplash.com/photos/_FVhXBaK_zQ

Alô alô, meu povo! No post de hoje vamos aprender algumas coisas bem legais a respeito da linha curitibana de turismo (vulgo Jardineira) com o senhor Python. Até o final desse post você será capaz de responder duas perguntas:

  • Quantos pontos de ônibus ela possui?
  • Quantos quilômetros a jardineira percorre ao todo?

Além disso, você vai aprender também:

  • Calcular distância entre duas coordenadas geográficas através da API do Google Maps;
  • Deixar o seu mapa interativo e colocar marcadores.

Estão preparados?

Esse post é uma continuação do tema sobre o transporte público da cidade de Curitiba. — Caso você não tenha lido a parte 1 e parte 2, já dá uma lida para se inteirar no assunto.

Curitiba contém 11 linhas de ônibus: Alimentador, Convencional, Linha Direta (ou Ligeirinho para os íntimos), Madrugueiro, Troncal, Expresso, Interbairros, Ligeirão, Circular Centro, Evento Especial e Jardineira.

ALIMENTADOR        144
CONVENCIONAL 72
LINHA DIRETA 28
MADRUGUEIRO 19
TRONCAL 17
EXPRESSO 11
INTERBAIRROS 9
LIGEIRÃO 3
EVENTO ESPECIAL 2
CIRCULAR CENTRO 2
JARDINEIRA 1

Porém todavia entretanto no entanto, o foco de hoje é para a nossa belíssima Jardineira. Caso você não seja de Curitiba ou nunca tenha visitado a cidade, segue foto:

Fonte: https://www.urbs.curitiba.pr.gov.br/transporte/linha-turismo

Eu já até sei que a primeira coisa que você pensou ao olhar essa foto foi em querer andar na parte de cima do ônibus. Eu, você e toda a população da cidade temos o mesmo desejo. Tamo junto 🤙

Análise de 🎲

Observação: os dados utilizados para esse estudo é da data 01/01/2021.

Observação2: meu código está uma bagunça — prometo melhorar 🤡.

Dados sobre a linha de ônibus

Temos duas perguntas para responder, porém, o mais importante são os dados. Como que a gente consegue eles? 🌚

  • Primeiro passo: acessar esse site e baixar os json que constam abaixo;
# IMPORTANDO AS BASES DE DADOS - URBSdfShape = pd.read_json('2021_01_01_shapeLinha.json')
dfTrecho = pd.read_json('2021_01_01_trechosItinerarios.json')
dfPontos = pd.read_json('2021_01_01_pontosLinha.json')
dfLinhas = pd.read_json('2021_01_01_linhas.json')

Eu modifiquei e formatei os nomes de algumas colunas. Usei esses comandos:

# RENOMEANDO NOME DA COLUNAdfLinhas = dfLinhas.rename(columns={'NOME':'NOME_LINHA'})
dfPontos = dfPontos.rename(columns={'LAT':'LATITUDE'})
dfPontos = dfPontos.rename(columns={'LON':'LONGITUDE'})
# FORMATANDO AS COORDENADAS GEOGRÁFICASdfPontos['LATITUDE'] = dfPontos['LATITUDE'].str.replace(',', '.')
dfPontos['LONGITUDE'] = dfPontos['LONGITUDE'].str.replace(',', '.')
dfPontos['LATITUDE'] = dfPontos['LATITUDE'].astype(float)
dfPontos['LONGITUDE'] = dfPontos['LONGITUDE'].astype(float)
dfShape['LAT'] = dfShape['LAT'].str.replace(',', '.')
dfShape['LON'] = dfShape['LON'].str.replace(',', '.')
dfShape['LAT'] = dfShape['LAT'].astype(float)
dfShape['LON'] = dfShape['LON'].astype(float)
dfShape.head()

Aqui estão os pontos principais do código, ele está completo lá no meu GitHub. Caso fique alguma dúvida é só entrar em contato.

# LINHA TURISMO PELO DF PONTOSdf979 = dfPontos[dfPontos['COD']=='979']
df979.sort_values('SEQ',ascending=True, inplace=True)

Abaixo eu filtro um dos itinerários, pois ele tem a ida e volta (para calcular um circuito completo da Jardineira).

# FIltrando o itinerary_id pois nesse df tem a ida e a voltadf979 = df979[df979['ITINERARY_ID']==1065]
print('Quantidade de pontos da linha turismo 979:',df979['SEQ'].count())
df979.head()

Temos a resposta da nossa primeira pergunta! 👀🏄‍♀️

Fonte: Caroline Attilio

Ou seja, a Jardineira possui 28 pontos para você descer, dar um rolêzinho e voltar depois (ao fim do post tem um mapa top da balada para você conferir os locais dos pontos de ônibus).

Fonte: Caroline Attilio

Essa é a carinha do nosso DataFrame.

# CRIANDO AS COORDENADASdf979['COORDENADA'] = list(zip(df979.LATITUDE, df979.LONGITUDE))
Fonte: Caroline Attilio

Eu acabo nem usando a coluna ‘COORDENADA’, mas caso um dia você precise dessa informação você já sabe como fazer.

# Aqui eu crio colunas com a latitude e longitude da próxima parada, para depois através da API do Google Maps calcular a distânciadf979['LATITUDE_FINAL'] = df979['LATITUDE'].shift(-1)
df979['LONGITUDE_FINAL'] = df979['LONGITUDE'].shift(-1)
Fonte: Caroline Attilio

API Google Maps

  • Você precisa criar uma chave de acesso no Google Cloud Platform;
  • Agora só alegria:
google_key = 'VOCÊCOLOCA SUA CHAVE AQUI'lat_origem = df979['LATITUDE'].tolist()
long_origem = df979['LONGITUDE'].tolist()
lat_destino = df979['LATITUDE_FINAL'].tolist()
long_destino = df979['LONGITUDE_FINAL'].tolist()
distancia = []
for i in range(len(long_destino)):
url = f"https://maps.googleapis.com/maps/api/distancematrix/json?units=metric&origins={lat_origem[i]},{long_origem[i]}&destinations={lat_destino[i]}%2C{long_destino[i]}&key={google_key}"
r=requests.get(url)
data = r.json()
try:
distancia.append(data['rows'][0]['elements'][0]['distance']['text'])
except:
pass
distancia

Caso você dê outro nome ao seu DataFrame, basta modificar no meu código o df979 (o destaque em verde)para dfnomequevcquiser.

Fonte: Caroline Attilio

Outro ponto importante aqui é o units=metric, ele retorna os valores em quilômetros. Caso você queira em milhas basta mudar para units=imperial.

Como nem tudo são flores, o output da imagem acima vem com 1 valor a menos, temos 28 pontos de ônibus mas ele devolve 27. Por quê?

Eu coloco a origem e destino na mesma linha, masssssss a última linha não tem nenhum destino. Ela está vazia. Caso você só acredite vendo, então veja:

Fonte: Caroline Attilio

E AGORA? A gente senta e chora? Se quiser pode, ninguém vai te julgar! Entretanto, temos uma solução!

distancia.append('0 km')

Eu coloquei um ‘0 km’ ao fim da lista e cheirinho de sucesso.

# ESSA PARTE EU SEPARO O VALOR DA DISTÂNCIA ENTRE OS PONTOS E ADICIONO EM UMA COLUNA NO DF979 COM OS OUTROS DADOSdistancia2 = []
for i in range(len(distancia)):
distancia2.append(float((distancia[i].replace(' km', ''))))
df979['DIST_KM'] = distancia2
df979.head()

Agora vou tirar a prova e ver se realmente bate com os resultados do Google Maps.

Fonte: Caroline Attilio

Vou pegar a LATITUDE e LONGITUDE como ponto de partida e LATITUDE_FINAL e LONGITUDE_FINAL como ponto de chegada.Tem que dar aproximadamente 1km essa distância. SERÁAAAAAAAAAA QUE VAI DAR? Façam suas apostas.

Fonte: Caroline Attilio

Para dar mais uma conferida, vamos tentar outros pontos.

Fonte: Caroline Attilio
Fonte: Caroline Attilio

Mágica? Não! Python.

Agora com todos os dados fica bem fácil saber o quanto que a nossa Jardineira roda em km num circuito.

df979[‘DIST_KM’].sum()

Resultado: 45,7km!

Agora se alguém te perguntar você já sabe duas curiosidades sobre a nossa cidade das terras das capivaras.

Final feliz 💕

Mapa

mapa979 = folium.Map(location=[-25.4416481,-49.3481283], zoom_start=12,
tiles='OpenStreetMap')
for i, v in Shape979.iterrows():
folium.CircleMarker(location=[v['LAT'], v['LON']],
radius=3,
color='#fc081c',
fill_color='#fc081c',
fill_opacity=0.1,
fill=True).add_to(mapa979)

folium.PolyLine(locations=Shape979[{'LAT','LON'}], color="#fc081c", weight=7, opacity=10).add_to(mapa979)
mapa979
Rota da Jardineira
mapa979 = folium.Map(location=[-25.4416481,-49.3481283], zoom_start=12,
tiles='OpenStreetMap')
for i, v in Shape979.iterrows():
folium.CircleMarker(location=[v['LAT'], v['LON']],
radius=3,
color='#fc081c',
fill_color='#fc081c',
fill_opacity=0.1,
fill=True).add_to(mapa979)

folium.PolyLine(locations=Shape979[{'LAT','LON'}], color="#fc081c", weight=7, opacity=10).add_to(mapa979)
for i in range(0,len(df979)):
folium.Marker(
location=[df979.iloc[i]['LATITUDE'], df979.iloc[i]['LONGITUDE']],
popup=df979.iloc[i]['NOME'],
icon = folium.Icon(color='red', icon='info-sign')
).add_to(mapa979)
mapa979
Rota da Jardineira com os pontos de ônibus
mapa979 = folium.Map(location=[-25.4416481,-49.3481283], zoom_start=12,
tiles='OpenStreetMap')
for i, v in Shape979.iterrows():
folium.CircleMarker(location=[v['LAT'], v['LON']],
radius=3,
color='#fc081c',
fill_color='#fc081c',
fill_opacity=0.1,
fill=True).add_to(mapa979)

folium.PolyLine(locations=Shape979[{'LAT','LON'}], color="#fc081c", weight=7, opacity=10).add_to(mapa979)
for i in range(0,len(df979)):
folium.Marker(
location=[df979.iloc[i]['LATITUDE'], df979.iloc[i]['LONGITUDE']],
popup=df979.iloc[i]['NOME'],
icon = folium.Icon(color='red', icon='info-sign')
).add_to(mapa979)
folium.Marker(
location = ['-25.4420753','-49.2387704'],
popup = 'Jardim Botânico',
icon = folium.Icon(color='green', icon='camera')
).add_to(mapa979)
folium.Marker(
location = ['-25.3846139','-49.2784345'],
popup = 'Ópera de Arame',
icon = folium.Icon(color='green', icon='camera')
).add_to(mapa979)
folium.Marker(
location = ['-25.4100978','-49.2693819'],
popup = 'Museu Oscar Niemeyer',
icon = folium.Icon(color='green', icon='camera')
).add_to(mapa979)
#folium.LayerControl(collapsed=False).add_to(mapa979)
#mapa979.save('jardineira_turismo_curitiba.html')
mapa979
Rota da Jardineira com alguns pontos turísticos

É isso, pessoal! Espero que tenham gostado. Estou aprendendo sobre o mundo dos dados, então qualquer feedback e cometário ele é muito bem-vindo. Obrigada pelo seu tempo em ler, curta e compartilhe.

Gratiluz ✨💕

--

--

Caroline Attilio

engineering student | data lover | seamstress in my free time