{
  "title": "PhotoRouter plan",
  "description": "A photo trip: cities, photo spots with coordinates and light, and which spot to shoot in each time slot of each day.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "version",
    "title",
    "summary",
    "timezone",
    "cities",
    "spots",
    "days",
    "skipped"
  ],
  "properties": {
    "version": {
      "type": "string",
      "const": "photorouter.plan/v2"
    },
    "title": {
      "type": "string",
      "description": "A short name for the trip, e.g. \"Two cities by train in spring\"."
    },
    "summary": {
      "type": "string",
      "description": "Two or three sentences to the photographer about the route."
    },
    "timezone": {
      "type": "string",
      "description": "IANA time zone of the destination, in Region/City form."
    },
    "cities": {
      "type": "array",
      "description": "Every city the trip stays in, in the order they are first visited.",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "localName",
          "lat",
          "lng",
          "base"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Short lowercase id, e.g. \"city1\"."
          },
          "name": {
            "type": "string",
            "description": "City name in English."
          },
          "localName": {
            "anyOf": [
              {
                "type": "string",
                "description": "Name in the local language or script, when it differs."
              },
              {
                "type": "null"
              }
            ]
          },
          "lat": {
            "type": "number",
            "description": "Latitude of the city centre."
          },
          "lng": {
            "type": "number",
            "description": "Longitude of the city centre."
          },
          "base": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "label",
              "lat",
              "lng"
            ],
            "properties": {
              "label": {
                "type": "string",
                "description": "Where to stay, e.g. \"near the old town\"."
              },
              "lat": {
                "type": "number",
                "description": "Latitude of the suggested base."
              },
              "lng": {
                "type": "number",
                "description": "Longitude of the suggested base."
              }
            }
          }
        }
      }
    },
    "spots": {
      "type": "array",
      "description": "Every photo spot used in \"days\". Real places with accurate coordinates.",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "cityId",
          "name",
          "localName",
          "lat",
          "lng",
          "light",
          "lights",
          "when",
          "tip",
          "styles",
          "focal",
          "tripod",
          "minutes",
          "effort",
          "crowd",
          "drone",
          "hike"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Short lowercase id, unique in the trip, e.g. \"harbour-steps\"."
          },
          "cityId": {
            "type": "string",
            "description": "Id of the city the spot belongs to."
          },
          "name": {
            "type": "string",
            "description": "Name in English."
          },
          "localName": {
            "anyOf": [
              {
                "type": "string",
                "description": "Name in the local language or script, for taxis and signs, when it differs."
              },
              {
                "type": "null"
              }
            ]
          },
          "lat": {
            "type": "number",
            "description": "Latitude of the viewpoint."
          },
          "lng": {
            "type": "number",
            "description": "Longitude of the viewpoint."
          },
          "light": {
            "type": "string",
            "enum": [
              "dawn",
              "day",
              "golden",
              "night"
            ],
            "description": "The best light for this spot."
          },
          "lights": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "dawn",
                "day",
                "golden",
                "night"
              ]
            },
            "description": "Every light that works here, including \"light\"."
          },
          "when": {
            "type": "string",
            "description": "Best time in a few words, e.g. \"Sunrise, from the river side\"."
          },
          "tip": {
            "type": "string",
            "description": "One or two sentences: where to stand and what to shoot."
          },
          "styles": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "street",
                "night",
                "cityscape",
                "architecture",
                "heritage",
                "people",
                "long-exposure",
                "landscape",
                "aerial"
              ]
            }
          },
          "focal": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "ultrawide",
                "wide",
                "standard",
                "tele"
              ]
            },
            "description": "Focal ranges that work, best first."
          },
          "tripod": {
            "type": "string",
            "enum": [
              "needed",
              "helps",
              "no",
              "banned"
            ]
          },
          "minutes": {
            "type": "integer",
            "description": "Time to give the spot, travel to it excluded."
          },
          "effort": {
            "type": "integer",
            "enum": [
              1,
              2,
              3
            ],
            "description": "1 walk-up, 2 a climb or a ride out, 3 a half-day trip."
          },
          "crowd": {
            "type": "integer",
            "enum": [
              1,
              2,
              3
            ],
            "description": "1 quiet, 3 one of the busiest places in the city."
          },
          "drone": {
            "type": "boolean",
            "description": "Drones are allowed and it is worth flying."
          },
          "hike": {
            "type": "boolean",
            "description": "Getting there is a real hike, not a short climb."
          }
        }
      }
    },
    "days": {
      "type": "array",
      "description": "One entry per trip date, in date order, including days with no slots.",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "date",
          "cityId",
          "title",
          "travel",
          "dayTrip",
          "slots"
        ],
        "properties": {
          "date": {
            "type": "string",
            "description": "YYYY-MM-DD"
          },
          "cityId": {
            "type": "string",
            "description": "The city slept in that night."
          },
          "title": {
            "anyOf": [
              {
                "type": "string",
                "description": "A short headline for the day."
              },
              {
                "type": "null"
              }
            ]
          },
          "travel": {
            "anyOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "fromCityId",
                  "mode",
                  "label",
                  "minutes"
                ],
                "properties": {
                  "fromCityId": {
                    "anyOf": [
                      {
                        "type": "string",
                        "description": "City left that morning; null on the first day, arriving from home."
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "train",
                      "ferry",
                      "flight",
                      "bus",
                      "car"
                    ]
                  },
                  "label": {
                    "type": "string",
                    "description": "E.g. \"High-speed train\" or \"Flight from home\"."
                  },
                  "minutes": {
                    "anyOf": [
                      {
                        "type": "integer",
                        "description": "Door-to-door time, roughly."
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "dayTrip": {
            "anyOf": [
              {
                "type": "object",
                "description": "A trip to another city and back on the same day, sleeping in \"cityId\". Not on the first day, and not together with \"travel\".",
                "additionalProperties": false,
                "required": [
                  "cityId",
                  "mode",
                  "label",
                  "minutes"
                ],
                "properties": {
                  "cityId": {
                    "type": "string",
                    "description": "The city visited for the day; list it in \"cities\"."
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "train",
                      "ferry",
                      "flight",
                      "bus",
                      "car"
                    ]
                  },
                  "label": {
                    "type": "string",
                    "description": "E.g. \"Regional train\"."
                  },
                  "minutes": {
                    "anyOf": [
                      {
                        "type": "integer",
                        "description": "Time each way, roughly."
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "slots": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "label",
                "spotId",
                "why",
                "note"
              ],
              "properties": {
                "label": {
                  "type": "string",
                  "enum": [
                    "Before work",
                    "Morning",
                    "Midday",
                    "Afternoon",
                    "Late afternoon",
                    "Blue hour",
                    "Sunset",
                    "After work",
                    "Night",
                    "Late"
                  ]
                },
                "spotId": {
                  "type": "string",
                  "description": "Id of a spot in \"spots\"."
                },
                "why": {
                  "type": "string",
                  "description": "Why this spot, in this slot, for this photographer."
                },
                "note": {
                  "anyOf": [
                    {
                      "type": "string",
                      "description": "An optional practical tip."
                    },
                    {
                      "type": "null"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "skipped": {
      "type": "array",
      "description": "Notable places left out, with the reason.",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "reason"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "reason": {
            "type": "string"
          }
        }
      }
    }
  }
}