diff --git a/src/libs/routes/location.js b/src/libs/routes/location.js
new file mode 100644
index 0000000000000000000000000000000000000000..a42892721a38812a51983d6d12850f55beac50f8
--- /dev/null
+++ b/src/libs/routes/location.js
@@ -0,0 +1,657 @@
+const express = require('express');
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const log = require(`${libs}/log`)(module);
+
+const query = require(`${libs}/middlewares/query`);
+
+const sqlQuery = require(`${libs}/db/query_exec`);
+
+const response = require(`${libs}/middlewares/response`);
+
+const locationApp = express();
+
+function locationIdToStr(locationId) {
+    let locationStr = 'Todas';
+    switch(locationId) {
+        case 1:
+            locationStr = 'Urbana';
+            break;
+        case 2:
+            locationStr = 'Rural';
+            break;
+    }
+    return locationStr;
+}
+
+locationApp.get('/sociodemographic', (req, res, next) => {
+    const populationYearQry = squel.select()
+        .field('MAX(ibge_populacao.ano_censo)')
+        .from('ibge_populacao');
+
+    const populationQry = squel.select()
+        .field('\'Brasil\'', 'location')
+        .field('SUM(populacao)', 'population')
+        .field('ibge_populacao.ano_censo')
+        .from('ibge_populacao')
+        .where(`ibge_populacao.ano_censo IN (${populationYearQry.toString()})`)
+        .group('ibge_populacao.ano_censo');
+
+    const pibYearQry = squel.select()
+        .field('MAX(ibge_pib.ano_censo)')
+        .from('ibge_pib');
+
+    const pibQry = squel.select()
+        .field('\'Brasil\'', 'location')
+        .field('AVG(ibge_pib.pib_per_capita)', 'gdp_per_capita')
+        .field('ibge_pib.ano_censo')
+        .from('ibge_pib')
+        .where(`ibge_pib.ano_censo IN (${pibYearQry.toString()})`)
+        .group('ibge_pib.ano_censo');
+
+    const idhYearQry = squel.select()
+        .field('MAX(adh_idh.ano_censo)')
+        .from('adh_idh');
+
+    const idhQry = squel.select()
+        .field('\'Brasil\'', 'location')
+        .field('AVG(idhm)', 'idhm')
+        .field('adh_idh.ano_censo')
+        .from('adh_idh')
+        .where(`adh_idh.ano_censo IN (${idhYearQry.toString()})`)
+        .group('adh_idh.ano_censo');
+
+    const analfabYearQry = squel.select()
+        .field('MAX(adh_analfabetismo.ano_censo)')
+        .from('adh_analfabetismo');
+
+    const analfabQry = squel.select()
+        .field('\'Brasil\'', 'location')
+        .field('AVG(t_analf15m)', 'analfabetism')
+        .field('adh_analfabetismo.ano_censo')
+        .from('adh_analfabetismo')
+        .where(`adh_analfabetismo.ano_censo IN (${analfabYearQry.toString()})`)
+        .group('adh_analfabetismo.ano_censo');
+
+    const giniYearQry = squel.select()
+        .field('MAX(adh_gini.ano_censo)')
+        .from('adh_gini');
+
+    const giniQry = squel.select()
+        .field('\'Brasil\'', 'location')
+        .field('AVG(gini)', 'gini')
+        .field('adh_gini.ano_censo')
+        .from('adh_gini')
+        .where(`adh_gini.ano_censo IN (${giniYearQry.toString()})`)
+        .group('adh_gini.ano_censo');
+
+    const queries = [populationQry, pibQry, idhQry, analfabQry, giniQry].map((qry) => {
+        return sqlQuery(qry.toString());
+    });
+
+    // execute all queries concurrently
+    Promise.all(queries).then((queryResults) => {
+        req.result = [];
+        for(let result of queryResults) {
+            log.debug(result);
+            req.result.push(result[0]);
+        }
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/sociodemographic/region/:id', (req, res, next) => {
+    const regionId = parseInt(req.params.id, 10);
+
+    const populationYearQry = squel.select()
+        .field('MAX(ibge_populacao.ano_censo)')
+        .from('ibge_populacao');
+
+    const populationQry = squel.select()
+        .field('regiao.nome', 'location')
+        .field('SUM(populacao)', 'population')
+        .field('ano_censo', 'census_year')
+        .from('ibge_populacao')
+        .from('municipio')
+        .from('estado')
+        .from('regiao')
+        .where(`regiao.pk_regiao_id = ${regionId}`)
+        .where(`ibge_populacao.ano_censo IN (${populationYearQry.toString()})`)
+        .where('ibge_populacao.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .where('estado.fk_regiao_id = regiao.pk_regiao_id')
+        .group('regiao.nome')
+        .group('ibge_populacao.ano_censo')
+        .order('regiao.nome');
+
+    const pibYearQry = squel.select()
+        .field('MAX(ibge_pib.ano_censo)')
+        .from('ibge_pib');
+
+    const pibQry = squel.select()
+        .field('regiao.nome', 'location')
+        .field('AVG(ibge_pib.pib_per_capita)', 'gdp_per_capita')
+        .field('ano_censo', 'census_year')
+        .from('ibge_pib')
+        .from('municipio')
+        .from('estado')
+        .from('regiao')
+        .where(`regiao.pk_regiao_id = ${regionId}`)
+        .where(`ibge_pib.ano_censo IN (${pibYearQry.toString()})`)
+        .where('ibge_pib.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .where('estado.fk_regiao_id = regiao.pk_regiao_id')
+        .group('regiao.nome')
+        .group('ibge_pib.ano_censo')
+        .order('regiao.nome');
+
+    const idhYearQry = squel.select()
+        .field('MAX(adh_idh.ano_censo)')
+        .from('adh_idh');
+
+    const idhQry = squel.select()
+        .field('regiao.nome', 'location')
+        .field('AVG(idhm)', 'idhm')
+        .field('ano_censo', 'census_year')
+        .from('adh_idh')
+        .from('municipio')
+        .from('estado')
+        .from('regiao')
+        .where(`regiao.pk_regiao_id = ${regionId}`)
+        .where(`adh_idh.ano_censo IN (${idhYearQry.toString()})`)
+        .where('adh_idh.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .where('estado.fk_regiao_id = regiao.pk_regiao_id')
+        .group('regiao.nome')
+        .group('adh_idh.ano_censo')
+        .order('regiao.nome');
+
+    const analfabYearQry = squel.select()
+        .field('MAX(adh_analfabetismo.ano_censo)')
+        .from('adh_analfabetismo');
+
+    const analfabQry = squel.select()
+        .field('regiao.nome', 'location')
+        .field('AVG(t_analf15m)', 'analfabetism')
+        .field('ano_censo', 'census_year')
+        .from('adh_analfabetismo')
+        .from('municipio')
+        .from('estado')
+        .from('regiao')
+        .where(`regiao.pk_regiao_id = ${regionId}`)
+        .where(`adh_analfabetismo.ano_censo IN (${analfabYearQry.toString()})`)
+        .where('adh_analfabetismo.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .where('estado.fk_regiao_id = regiao.pk_regiao_id')
+        .group('regiao.nome')
+        .group('adh_analfabetismo.ano_censo')
+        .order('regiao.nome');
+
+    const giniYearQry = squel.select()
+        .field('MAX(adh_gini.ano_censo)')
+        .from('adh_gini');
+
+    const giniQry = squel.select()
+        .field('regiao.nome', 'location')
+        .field('AVG(gini)', 'gini')
+        .field('ano_censo', 'census_year')
+        .from('adh_gini')
+        .from('municipio')
+        .from('estado')
+        .from('regiao')
+        .where(`regiao.pk_regiao_id = ${regionId}`)
+        .where(`adh_gini.ano_censo IN (${giniYearQry.toString()})`)
+        .where('adh_gini.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .where('estado.fk_regiao_id = regiao.pk_regiao_id')
+        .group('regiao.nome')
+        .group('adh_gini.ano_censo')
+        .order('regiao.nome');
+
+    const queries = [populationQry, pibQry, idhQry, analfabQry, giniQry].map((qry) => {
+        return sqlQuery(qry.toString());
+    });
+
+    // execute all queries concurrently
+    Promise.all(queries).then((queryResults) => {
+        req.result = [];
+        for(let result of queryResults) {
+            log.debug(result);
+            req.result.push(result[0]);
+        }
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/sociodemographic/state/:id', (req, res, next) => {
+    const stateId = parseInt(req.params.id, 10);
+
+    const populationYearQry = squel.select()
+        .field('MAX(ibge_populacao.ano_censo)')
+        .from('ibge_populacao');
+    // load all cities by state and compute the sociodemographic and educational information
+    const populationQry = squel.select()
+        .field('estado.nome', 'location')
+        .field('SUM(populacao)', 'population')
+        .field('ibge_populacao.ano_censo', 'census_year')
+        .from('ibge_populacao')
+        .from('municipio')
+        .from('estado')
+        .where(`estado.pk_estado_id = ${stateId}`)
+        .where(`ibge_populacao.ano_censo IN (${populationYearQry.toString()})`)
+        .where('ibge_populacao.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .group('estado.nome')
+        .group('ibge_populacao.ano_censo')
+        .order('estado.nome');
+
+    const pibYearQry = squel.select()
+        .field('MAX(ibge_pib.ano_censo)')
+        .from('ibge_pib');
+
+    const pibQry = squel.select()
+        .field('estado.nome', 'location')
+        .field('AVG(ibge_pib.pib_per_capita)', 'gdp_per_capita')
+        .field('ibge_pib.ano_censo', 'census_year')
+        .from('ibge_pib')
+        .from('municipio')
+        .from('estado')
+        .where(`estado.pk_estado_id = ${stateId}`)
+        .where(`ibge_pib.ano_censo IN (${pibYearQry.toString()})`)
+        .where('ibge_pib.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .group('estado.nome')
+        .group('ibge_pib.ano_censo')
+        .order('estado.nome');
+
+    const idhYearQry = squel.select()
+        .field('MAX(adh_idh.ano_censo)')
+        .from('adh_idh');
+
+    const idhQry = squel.select()
+        .field('estado.nome', 'location')
+        .field('AVG(idhm)', 'idhm')
+        .field('adh_idh.ano_censo', 'census_year')
+        .from('adh_idh')
+        .from('municipio')
+        .from('estado')
+        .where(`estado.pk_estado_id = ${stateId}`)
+        .where(`adh_idh.ano_censo IN (${idhYearQry.toString()})`)
+        .where('adh_idh.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .group('estado.nome')
+        .group('adh_idh.ano_censo')
+        .order('estado.nome');
+
+    const analfabYearQry = squel.select()
+        .field('MAX(adh_analfabetismo.ano_censo)')
+        .from('adh_analfabetismo');
+
+    const analfabQry = squel.select()
+        .field('estado.nome', 'location')
+        .field('AVG(t_analf15m)', 'analfabetism')
+        .field('adh_analfabetismo.ano_censo', 'census_year')
+        .from('adh_analfabetismo')
+        .from('municipio')
+        .from('estado')
+        .where(`estado.pk_estado_id = ${stateId}`)
+        .where(`adh_analfabetismo.ano_censo IN (${analfabYearQry.toString()})`)
+        .where('adh_analfabetismo.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .group('estado.nome')
+        .group('adh_analfabetismo.ano_censo')
+        .order('estado.nome');
+
+    const giniYearQry = squel.select()
+        .field('MAX(adh_gini.ano_censo)')
+        .from('adh_gini');
+
+    const giniQry = squel.select()
+        .field('estado.nome', 'location')
+        .field('AVG(gini)', 'gini')
+        .field('adh_gini.ano_censo', 'census_year')
+        .from('adh_gini')
+        .from('municipio')
+        .from('estado')
+        .where(`estado.pk_estado_id = ${stateId}`)
+        .where(`adh_gini.ano_censo IN (${giniYearQry.toString()})`)
+        .where('adh_gini.fk_municipio_id = municipio.pk_cod_ibge')
+        .where('municipio.fk_estado_id = estado.pk_estado_id')
+        .group('estado.nome')
+        .group('adh_gini.ano_censo')
+        .order('estado.nome');
+
+    const queries = [populationQry, pibQry, idhQry, analfabQry, giniQry].map((qry) => {
+        return sqlQuery(qry.toString());
+    });
+
+    // execute all queries concurrently
+    Promise.all(queries).then((queryResults) => {
+        req.result = [];
+        for(let result of queryResults) {
+            log.debug(result);
+            req.result.push(result[0]);
+        }
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/sociodemographic/city/:id', (req, res, next) => {
+    const cityId = parseInt(req.params.id, 10);
+
+    const populationYearQry = squel.select()
+        .field('MAX(ibge_populacao.ano_censo)')
+        .from('ibge_populacao');
+    // load all cities by state and compute the sociodemographic and educational information
+    const populationQry = squel.select()
+        .field('municipio.nome', 'location')
+        .field('SUM(populacao)', 'population')
+        .field('ibge_populacao.ano_censo', 'census_year')
+        .from('ibge_populacao')
+        .from('municipio')
+        .where(`municipio.pk_cod_ibge = ${cityId}`)
+        .where(`ibge_populacao.ano_censo IN (${populationYearQry.toString()})`)
+        .where('ibge_populacao.fk_municipio_id = municipio.pk_cod_ibge')
+        .group('municipio.nome')
+        .group('ibge_populacao.ano_censo')
+        .order('municipio.nome');
+
+    const pibYearQry = squel.select()
+        .field('MAX(ibge_pib.ano_censo)')
+        .from('ibge_pib');
+
+    const pibQry = squel.select()
+        .field('municipio.nome', 'location')
+        .field('AVG(ibge_pib.pib_per_capita)', 'gdp_per_capita')
+        .field('ibge_pib.ano_censo', 'census_year')
+        .from('ibge_pib')
+        .from('municipio')
+        .where(`municipio.pk_cod_ibge = ${cityId}`)
+        .where(`ibge_pib.ano_censo IN (${pibYearQry.toString()})`)
+        .where('ibge_pib.fk_municipio_id = municipio.pk_cod_ibge')
+        .group('municipio.nome')
+        .group('ibge_pib.ano_censo')
+        .order('municipio.nome');
+
+    const idhYearQry = squel.select()
+        .field('MAX(adh_idh.ano_censo)')
+        .from('adh_idh');
+
+    const idhQry = squel.select()
+        .field('municipio.nome', 'location')
+        .field('AVG(idhm)', 'idhm')
+        .field('adh_idh.ano_censo', 'census_year')
+        .from('adh_idh')
+        .from('municipio')
+        .where(`municipio.pk_cod_ibge = ${cityId}`)
+        .where(`adh_idh.ano_censo IN (${idhYearQry.toString()})`)
+        .where('adh_idh.fk_municipio_id = municipio.pk_cod_ibge')
+        .group('municipio.nome')
+        .group('adh_idh.ano_censo')
+        .order('municipio.nome');
+
+    const analfabYearQry = squel.select()
+        .field('MAX(adh_analfabetismo.ano_censo)')
+        .from('adh_analfabetismo');
+
+    const analfabQry = squel.select()
+        .field('municipio.nome', 'location')
+        .field('AVG(t_analf15m)', 'analfabetism')
+        .field('adh_analfabetismo.ano_censo', 'census_year')
+        .from('adh_analfabetismo')
+        .from('municipio')
+        .where(`municipio.pk_cod_ibge = ${cityId}`)
+        .where(`adh_analfabetismo.ano_censo IN (${analfabYearQry.toString()})`)
+        .where('adh_analfabetismo.fk_municipio_id = municipio.pk_cod_ibge')
+        .group('municipio.nome')
+        .group('adh_analfabetismo.ano_censo')
+        .order('municipio.nome');
+
+    const giniYearQry = squel.select()
+        .field('MAX(adh_gini.ano_censo)')
+        .from('adh_gini');
+
+    const giniQry = squel.select()
+        .field('municipio.nome', 'location')
+        .field('AVG(gini)', 'gini')
+        .field('adh_gini.ano_censo', 'census_year')
+        .from('adh_gini')
+        .from('municipio')
+        .where(`municipio.pk_cod_ibge = ${cityId}`)
+        .where(`adh_gini.ano_censo IN (${giniYearQry.toString()})`)
+        .where('adh_gini.fk_municipio_id = municipio.pk_cod_ibge')
+        .group('municipio.nome')
+        .group('adh_gini.ano_censo')
+        .order('municipio.nome');
+
+    const queries = [populationQry, pibQry, idhQry, analfabQry, giniQry].map((qry) => {
+        return sqlQuery(qry.toString());
+    });
+
+    // execute all queries concurrently
+    Promise.all(queries).then((queryResults) => {
+        req.result = [];
+        for(let result of queryResults) {
+            log.debug(result);
+            req.result.push(result[0]);
+        }
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/educational', (req, res, next) => {
+    const censusYearQry = squel.select()
+        .field('MAX(escola.ano_censo)', 'ano_censo')
+        .from('escola')
+        .toString();
+
+    const totalSchoolsQry = squel.select()
+        .field('0', 'location')
+        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .from('escola')
+        .where(`escola.ano_censo IN (${censusYearQry})`);
+
+    const schoolsPerLocationQry = squel.select()
+        .field('escola.id_localizacao', 'location')
+        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .from('escola')
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .group('escola.id_localizacao');
+
+    const queries = [totalSchoolsQry, schoolsPerLocationQry].map((qry) => {
+        return sqlQuery(qry.toString());
+    });
+
+    // execute all queries concurrently
+    Promise.all(queries).then((queryResults) => {
+        req.result = [];
+        for(let result of queryResults) {
+            log.debug(result);
+            for(let row of result) {
+                row.location = locationIdToStr(row.location);
+                req.result.push(row);
+            }
+        }
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/educational/region/:id', (req, res, next) => {
+    const regionId = parseInt(req.params.id, 10);
+
+    const censusYearQry = squel.select()
+        .field('MAX(escola.ano_censo)', 'ano_censo')
+        .from('escola')
+        .toString();
+
+    const totalSchoolsQry = squel.select()
+        .field('regiao.nome')
+        .field('0', 'location')
+        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .from('escola')
+        .from('estado')
+        .from('regiao')
+        .where('escola.fk_estado_id = estado.pk_estado_id')
+        .where('estado.fk_regiao_id = regiao.pk_regiao_id')
+        .where(`regiao.pk_regiao_id = ${regionId}`)
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .group('regiao.nome');
+
+    const schoolsPerLocationQry = squel.select()
+        .field('regiao.nome')
+        .field('escola.id_localizacao', 'location')
+        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .from('escola')
+        .from('estado')
+        .from('regiao')
+        .where('escola.fk_estado_id = estado.pk_estado_id')
+        .where('estado.fk_regiao_id = regiao.pk_regiao_id')
+        .where(`regiao.pk_regiao_id = ${regionId}`)
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .group('regiao.nome')
+        .group('escola.id_localizacao');
+
+    const queries = [totalSchoolsQry, schoolsPerLocationQry].map((qry) => {
+        return sqlQuery(qry.toString());
+    });
+
+    // execute all queries concurrently
+    Promise.all(queries).then((queryResults) => {
+        req.result = [];
+        for(let result of queryResults) {
+            log.debug(result);
+            for(let row of result) {
+                row.location = locationIdToStr(row.location);
+                req.result.push(row);
+            }
+        }
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/educational/state/:id', (req, res, next) => {
+    const stateId = parseInt(req.params.id, 10);
+
+    const censusYearQry = squel.select()
+        .field('MAX(escola.ano_censo)', 'ano_censo')
+        .from('escola')
+        .toString();
+
+    const totalSchoolsQry = squel.select()
+        .field('estado.nome')
+        .field('0', 'location')
+        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .from('escola')
+        .from('estado')
+        .where('escola.fk_estado_id = estado.pk_estado_id')
+        .where(`escola.fk_estado_id = ${stateId}`)
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .group('estado.nome');
+
+    const schoolsPerLocationQry = squel.select()
+        .field('estado.nome')
+        .field('escola.id_localizacao', 'location')
+        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .from('escola')
+        .from('estado')
+        .where('escola.fk_estado_id = estado.pk_estado_id')
+        .where(`escola.fk_estado_id = ${stateId}`)
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .group('estado.nome')
+        .group('escola.id_localizacao');
+
+    const queries = [totalSchoolsQry, schoolsPerLocationQry].map((qry) => {
+        return sqlQuery(qry.toString());
+    });
+
+    // execute all queries concurrently
+    Promise.all(queries).then((queryResults) => {
+        req.result = [];
+        for(let result of queryResults) {
+            log.debug(result);
+            for(let row of result) {
+                row.location = locationIdToStr(row.location);
+                req.result.push(row);
+            }
+        }
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+locationApp.get('/educational/city/:id', (req, res, next) => {
+    const cityId = parseInt(req.params.id, 10);
+
+    const censusYearQry = squel.select()
+        .field('MAX(escola.ano_censo)', 'ano_censo')
+        .from('escola')
+        .toString();
+
+    const totalSchoolsQry = squel.select()
+        .field('municipio.nome')
+        .field('0', 'location')
+        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .from('escola')
+        .from('municipio')
+        .where('escola.fk_municipio_id = municipio.pk_cod_ibge')
+        .where(`escola.fk_municipio_id = ${cityId}`)
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .group('municipio.nome');
+
+    const schoolsPerLocationQry = squel.select()
+        .field('municipio.nome')
+        .field('escola.id_localizacao', 'location')
+        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .from('escola')
+        .from('municipio')
+        .where('escola.fk_municipio_id = municipio.pk_cod_ibge')
+        .where(`escola.fk_municipio_id = ${cityId}`)
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .group('municipio.nome')
+        .group('escola.id_localizacao');
+
+    const queries = [totalSchoolsQry, schoolsPerLocationQry].map((qry) => {
+        return sqlQuery(qry.toString());
+    });
+
+    // execute all queries concurrently
+    Promise.all(queries).then((queryResults) => {
+        req.result = [];
+        for(let result of queryResults) {
+            log.debug(result);
+            for(let row of result) {
+                row.location = locationIdToStr(row.location);
+                req.result.push(row);
+            }
+        }
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('location'));
+
+module.exports = locationApp;