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

Upload Connecticut boundaries and parking lot GeoJSON files #317

Open
Eric-Arellano opened this issue Jan 28, 2025 · 0 comments
Open

Upload Connecticut boundaries and parking lot GeoJSON files #317

Eric-Arellano opened this issue Jan 28, 2025 · 0 comments

Comments

@Eric-Arellano
Copy link
Contributor

#316 uploaded the scorecard data for the CT map, but we still need to upload the GeoJSON.

How to work with this repo

Refer to the README for an overview of the monorepo structure & key commands: https://github.com/ParkingReformNetwork/parking-lot-map?tab=readme-ov-file#parking-lot-map

tl;dr:

City IDs

Cities/locations are identified by kebab-case, like Hartford - Downtown -> hartford-downtown. This ID is used so the map knows what data to load.

In TypeScript, given a name, you can use lodash-es with kebabCase:

export function parseCityIdFromJson(jsonCityName: string): string {
return kebabCase(jsonCityName);
}

In Python, given a name, you can use this code:

def name_to_id(v: str) -> str:
    return v.lower().replace(" - ", "-").replace(" ", "-")

City boundaries

We organize the city boundaries into one file: https://github.com/ParkingReformNetwork/parking-lot-map/blob/main/packages/ct/data/city-boundaries.geojson

Each city should be a single entry in Features, with the geometry.type set to Polygon. Each entry must have properties.id set to that place's ID.

The two current entries should be deleted and come from the main map.

The entries should be sorted alphabetically by city ID.

You'll probably want to reuse this code:

export async function updateCoordinates(
scriptCommand: string,
cityId: CityId,
addCity: boolean,
originalFilePath: string,
updateFilePath: string,
): Promise<void> {
let newData: FeatureCollection<Polygon, GeoJsonProperties>;
try {
const rawNewData = await fs.readFile(updateFilePath, "utf8");
newData = JSON.parse(rawNewData);
} catch (err: unknown) {
const { message } = err as Error;
throw new Error(
`Issue reading the update file path ${updateFilePath}: ${message}`,
);
}
if (!Array.isArray(newData.features) || newData.features.length !== 1) {
throw new Error(
"The script expects exactly one entry in `features` because you can only update one city at a time.",
);
}
const polygon = newData.features[0].geometry;
const newCoordinates = polygon.coordinates;
const newGeometryType = polygon.type;
let originalData: FeatureCollection<Polygon, GeoJsonProperties>;
try {
const rawOriginalData = await fs.readFile(originalFilePath, "utf8");
originalData = JSON.parse(rawOriginalData);
} catch (err: unknown) {
const { message } = err as Error;
throw new Error(
`Issue reading the original data file path ${originalFilePath}: ${message}`,
);
}
if (addCity) {
const newEntry = {
type: "Feature",
properties: { id: cityId },
geometry: { type: newGeometryType, coordinates: newCoordinates },
} as Feature<Polygon>;
originalData.features.push(newEntry);
} else {
const cityOriginalData = originalData.features.find(
(feature) => feature?.properties?.id === cityId,
);
if (!cityOriginalData) {
throw new Error(
`City not found in ${originalFilePath}. To add a new city, run again with the '--add' flag, e.g. npm run ${scriptCommand} -- 'My City, AZ' --add`,
);
}
cityOriginalData.geometry.coordinates = newCoordinates;
}

Consider using the script https://github.com/ParkingReformNetwork/parking-lot-map/blob/main/packages/scripts/src/add-city.ts.

Parking lots

Each city has its own file. The file name must be city-id.geojson in the folder https://github.com/ParkingReformNetwork/parking-lot-map/tree/main/packages/ct/data/parking-lots, like hartford-downtown.geojson.

The file does not need to set properties.id, although it doesn't really matter.

The file is expected to set the geometry to MultiPolygon:

"geometry": {
"type": "MultiPolygon",
"coordinates": [

You probably want to use this code:

export async function updateParkingLots(
cityId: CityId,
addCity: boolean,
originalFilePath: string,
updateFilePath: string,
): Promise<void> {
let newData;
try {
const rawNewData = await fs.readFile(originalFilePath, "utf8");
newData = JSON.parse(rawNewData);
} catch (err: unknown) {
const { message } = err as Error;
throw new Error(
`Issue reading the update file path parking-lots-update.geojson: ${message}`,
);
}
if (!Array.isArray(newData.features) || newData.features.length !== 1) {
throw new Error(
"The script expects exactly one entry in `features` because you can only update one city at a time.",
);
}
const newCoordinates = newData.features[0].geometry.coordinates;
const newGeometryType = newData.features[0].geometry.type;
if (!addCity) {
let originalData;
try {
const rawOriginalData = await fs.readFile(updateFilePath, "utf8");
originalData = JSON.parse(rawOriginalData);
} catch (err: unknown) {
const { message } = err as Error;
throw new Error(
`Issue reading the original data file path ${updateFilePath}: ${message}`,
);
}
originalData.geometry.coordinates = newCoordinates;
await fs.writeFile(updateFilePath, JSON.stringify(originalData, null, 2));
return;
}
const newFile = {
type: "Feature",
properties: { id: cityId },
geometry: { type: newGeometryType, coordinates: newCoordinates },
};
await fs.writeFile(updateFilePath, JSON.stringify(newFile, null, 2));
}

Or this script https://github.com/ParkingReformNetwork/parking-lot-map/blob/main/packages/scripts/src/add-city.ts.

Tip with the add city script

I suspect it will be easiest to use https://github.com/ParkingReformNetwork/parking-lot-map/blob/main/packages/scripts/src/add-city.ts. However, some thoughts:

  • Read https://github.com/ParkingReformNetwork/parking-lot-map/tree/main#add-a-new-city for how you're supposed to use the script
  • You'll want to automate calling the script, since it processes one city at a time
  • You should temporarily disable the code to add scorecard data. The scorecards are already set up and should not be modified
  • I'm not certain if the GeoJSON data provided to us is already in the correct format or if you will need to make any changes
  • Use pnpm fmt after running the script to pretty-format the GeoJSON

How to test it works

Run pnpm -F ct start. Manually drag around the map to a few (or all) of the places to ensure their parking lot data loads, which is lazy.

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

No branches or pull requests

1 participant