// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package google.maps.solar.v1;

import "google/api/annotations.proto";
import "google/api/client.proto";
import "google/api/httpbody.proto";
import "google/type/date.proto";
import "google/type/latlng.proto";
import "google/type/money.proto";

option csharp_namespace = "Google.Maps.Solar.V1";
option go_package = "cloud.google.com/go/maps/solar/apiv1/solarpb;solarpb";
option java_multiple_files = true;
option java_outer_classname = "SolarServiceProto";
option java_package = "com.google.maps.solar.v1";
option objc_class_prefix = "GGMPV1A";
option php_namespace = "Google\\Maps\\Solar\\V1";
option ruby_package = "Google::Maps::Solar::V1";

// Service definition for the Solar API.
service Solar {
  option (google.api.default_host) = "solar.googleapis.com";
  option (google.api.oauth_scopes) =
      "https://www.googleapis.com/auth/cloud-platform";

  // Locates the closest building to a query point. Returns an error with
  // code `NOT_FOUND` if there are no buildings within approximately 50m of the
  // query point.
  rpc FindClosestBuildingInsights(FindClosestBuildingInsightsRequest)
      returns (BuildingInsights) {
    option (google.api.http) = {
      get: "/v1/buildingInsights:findClosest"
    };
  }

  // Gets solar information for a region surrounding a location.
  // Returns an error with code `NOT_FOUND` if the location is outside
  // the coverage area.
  rpc GetDataLayers(GetDataLayersRequest) returns (DataLayers) {
    option (google.api.http) = {
      get: "/v1/dataLayers:get"
    };
  }

  // Returns an image by its ID.
  rpc GetGeoTiff(GetGeoTiffRequest) returns (google.api.HttpBody) {
    option (google.api.http) = {
      get: "/v1/geoTiff:get"
    };
  }
}

// Request message for `Solar.FindClosestBuildingInsights`.
message FindClosestBuildingInsightsRequest {
  // The longitude and latitude from which the API looks for the nearest known
  // building.
  google.type.LatLng location = 1;

  // The minimum quality level allowed in the results. No result with
  // lower quality than this will be returned. Not specifying this is
  // equivalent to restricting to HIGH quality only.
  ImageryQuality required_quality = 3;
}

// A bounding box in lat/lng coordinates.
message LatLngBox {
  // The southwest corner of the box.
  google.type.LatLng sw = 1;

  // The northeast corner of the box.
  google.type.LatLng ne = 2;
}

// Response message for `Solar.FindClosestBuildingInsights`.
// Information about the location, dimensions, and solar potential of a
// building.
message BuildingInsights {
  // The resource name for the building, of the format `building/<place ID>`.
  string name = 1;

  // A point near the center of the building.
  google.type.LatLng center = 2;

  // The bounding box of the building.
  LatLngBox bounding_box = 9;

  // Date that the underlying imagery was acquired. This is approximate.
  google.type.Date imagery_date = 3;

  // When processing was completed on this imagery.
  google.type.Date imagery_processed_date = 11;

  // Postal code (e.g., US zip code) this building is contained by.
  string postal_code = 4;

  // Administrative area 1 (e.g., in the US, the state) that contains this
  // building. For example, in the US, the abbreviation might be "MA" or "CA."
  string administrative_area = 5;

  // Statistical area (e.g., US census tract) this building is in.
  string statistical_area = 6;

  // Region code for the country (or region) this building is in.
  string region_code = 7;

  // Solar potential of the building.
  SolarPotential solar_potential = 8;

  // The quality of the imagery used to compute the data for this building.
  ImageryQuality imagery_quality = 10;
}

// Information about the solar potential of a building. A number of
// fields in this are defined in terms of "panels". The fields
// [panel_capacity_watts]
// [google.maps.solar.v1.SolarPotential.panel_capacity_watts],
// [panel_height_meters]
// [google.maps.solar.v1.SolarPotential.panel_height_meters],
// and [panel_width_meters]
// [google.maps.solar.v1.SolarPotential.panel_width_meters]
// describe the parameters of the model of panel used in these
// calculations.
message SolarPotential {
  // Size of the maximum array - that is, the maximum number of panels that
  // can fit on the roof.
  int32 max_array_panels_count = 1;

  // Capacity, in watts, of the panel used in the calculations.
  float panel_capacity_watts = 9;

  // Height, in meters in portrait orientation, of the panel used in
  // the calculations.
  float panel_height_meters = 10;

  // Width, in meters in portrait orientation, of the panel used in
  // the calculations.
  float panel_width_meters = 11;

  // The expected lifetime, in years, of the solar panels. This is
  // used in the financial calculations.
  int32 panel_lifetime_years = 12;

  // Size, in square meters, of the maximum array.
  float max_array_area_meters2 = 2;

  // Maximum number of sunshine hours received per year, by any point
  // on the roof. Sunshine hours are a measure of the total amount of
  // insolation (energy) received per year. 1 sunshine hour = 1 kWh per kW
  // (where kW refers to kW of capacity under Standard Testing Conditions).
  float max_sunshine_hours_per_year = 3;

  // Equivalent amount of CO2 produced per MWh of grid electricity. This
  // is a measure of the carbon intensity of grid electricity displaced
  // by solar electricity.
  float carbon_offset_factor_kg_per_mwh = 4;

  // Total size and sunlight quantiles for the part of the roof that
  // was assigned to some roof segment. Despite the name, this may not
  // include the entire building. See [building_stats]
  // [google.maps.solar.v1.SolarPotential.building_stats].
  SizeAndSunshineStats whole_roof_stats = 5;

  // Size and sunlight quantiles for the entire building, including
  // parts of the roof that were not assigned to some roof segment.
  // Because the orientations of these parts are not well
  // characterised, the roof area estimate is unreliable, but the
  // ground area estimate is reliable. It may be that a more reliable
  // whole building roof area can be obtained by scaling the roof area
  // from [whole_roof_stats]
  // [google.maps.solar.v1.SolarPotential.whole_roof_stats] by
  // the ratio of the ground areas of `building_stats` and
  // `whole_roof_stats`.
  SizeAndSunshineStats building_stats = 13;

  // Size and sunlight quantiles for each roof segment.
  repeated RoofSegmentSizeAndSunshineStats roof_segment_stats = 6;

  // Each [SolarPanel] [google.maps.solar.v1.SolarPanel]
  // describes a single solar panel. They are listed in the order that
  // the panel layout algorithm placed this. This is usually, though
  // not always, in decreasing order of annual energy production.
  repeated SolarPanel solar_panels = 14;

  // Each [SolarPanelConfig]
  // [google.maps.solar.v1.SolarPanelConfig] describes a
  // different arrangement of solar panels on the roof. They are in
  // order of increasing number of panels. The `SolarPanelConfig` with
  // [panels_count]
  // [google.maps.solar.v1.SolarPanelConfig.panels_count]=N is
  // based on the first N panels in the `solar_panels` list.
  repeated SolarPanelConfig solar_panel_configs = 7;

  // A [FinancialAnalysis]
  // [google.maps.solar.v1.FinancialAnalysis] gives the savings
  // from going solar assuming a given monthly bill and a given
  // electricity provider. They are in order of increasing order of
  // monthly bill amount. This field will be empty for buildings in
  // areas for which the Solar API does not have enough information to
  // perform financial computations.
  repeated FinancialAnalysis financial_analyses = 8;
}

// Information about the size and sunniness quantiles of a roof segment.
message RoofSegmentSizeAndSunshineStats {
  // Angle of the roof segment relative to the theoretical ground plane.
  // 0 = parallel to the ground, 90 = perpendicular to the ground.
  optional float pitch_degrees = 1;

  // Compass direction the roof segment is pointing in. 0 = North, 90 =
  // East, 180 = South. For a "flat" roof segment (`pitch_degrees` very
  // near 0), azimuth is not well defined, so for consistency, we define it
  // arbitrarily to be 0 (North).
  optional float azimuth_degrees = 2;

  // Total size and sunlight quantiles for the roof segment.
  SizeAndSunshineStats stats = 3;

  // A point near the center of the roof segment.
  google.type.LatLng center = 4;

  // The bounding box of the roof segment.
  LatLngBox bounding_box = 5;

  // The height of the roof segment plane, in meters above sea level,
  // at the point designated by `center`. Together with the pitch,
  // azimuth, and center location, this fully defines the roof segment
  // plane.
  optional float plane_height_at_center_meters = 6;
}

// Size and sunniness quantiles of a roof, or part of a roof.
message SizeAndSunshineStats {
  // The area of the roof or roof segment, in m^2. This is the roof area
  // (accounting for tilt), not the ground footprint area.
  float area_meters2 = 1;

  // Quantiles of the pointwise sunniness across the area. If there
  // are N values here, this represents the (N-1)-iles. For example,
  // if there are 5 values, then they would be the quartiles (min,
  // 25%, 50%, 75%, max). Values are in annual kWh/kW like
  // [max_sunshine_hours_per_year]
  // [google.maps.solar.v1.SolarPotential.max_sunshine_hours_per_year].
  repeated float sunshine_quantiles = 2;

  // The ground footprint area covered by the roof or roof segment, in m^2.
  float ground_area_meters2 = 3;
}

// SolarPanel describes the position, orientation, and production of a
// single solar panel. See the [panel_height_meters]
// [google.maps.solar.v1.SolarPotential.panel_height_meters],
// [panel_width_meters]
// [google.maps.solar.v1.SolarPotential.panel_width_meters],
// and [panel_capacity_watts]
// [google.maps.solar.v1.SolarPotential.panel_capacity_watts]
// fields in [SolarPotential]
// [google.maps.solar.v1.SolarPotential] for information on the
// parameters of the panel.
message SolarPanel {
  // The centre of the panel.
  google.type.LatLng center = 1;

  // The orientation of the panel.
  SolarPanelOrientation orientation = 2;

  // How much sunlight energy this layout captures over the course of a
  // year, in DC kWh.
  float yearly_energy_dc_kwh = 3;

  // Index in [roof_segment_stats]
  // [google.maps.solar.v1.SolarPotential.roof_segment_stats]
  // of the `RoofSegmentSizeAndSunshineStats` which corresponds to the
  // roof segment that this panel is placed on.
  optional int32 segment_index = 4;
}

// SolarPanelConfig describes a particular placement of solar panels
// on the roof.
message SolarPanelConfig {
  // Total number of panels. Note that this is redundant to (the sum
  // of) the corresponding fields in [roof_segment_summaries]
  // [google.maps.solar.v1.SolarPanelConfig.roof_segment_summaries].
  int32 panels_count = 1;

  // How much sunlight energy this layout captures over the course of a
  // year, in DC kWh, assuming the panels described above.
  float yearly_energy_dc_kwh = 2;

  // Information about the production of each roof segment that is carrying
  // at least one panel in this layout. `roof_segment_summaries[i]` describes
  // the i-th roof segment, including its size, expected production and
  // orientation.
  repeated RoofSegmentSummary roof_segment_summaries = 4;
}

// Information about a roof segment on the building, with some number of
// panels placed on it.
message RoofSegmentSummary {
  // Angle of the roof segment relative to the theoretical ground plane.
  // 0 = parallel to the ground, 90 = perpendicular to the ground.
  optional float pitch_degrees = 2;

  // Compass direction the roof segment is pointing in. 0 = North, 90 =
  // East, 180 = South. For a "flat" roof segment (`pitch_degrees` very
  // near 0), azimuth is not well defined, so for consistency, we define it
  // arbitrarily to be 0 (North).
  optional float azimuth_degrees = 3;

  // The total number of panels on this segment.
  int32 panels_count = 7;

  // How much sunlight energy this part of the layout captures over the
  // course of a year, in DC kWh, assuming the panels described above.
  float yearly_energy_dc_kwh = 8;

  // Index in [roof_segment_stats]
  // [google.maps.solar.v1.SolarPotential.roof_segment_stats]
  // of the corresponding `RoofSegmentSizeAndSunshineStats`.
  optional int32 segment_index = 9;
}

// Analysis of the cost and benefits of the optimum solar layout for a
// particular electric bill size.
message FinancialAnalysis {
  // The monthly electric bill this analysis assumes.
  google.type.Money monthly_bill = 3;

  // Whether this is the bill size selected to be the default bill for the
  // area this building is in. Exactly one `FinancialAnalysis` in
  // `BuildingSolarPotential` should have `default_bill` set.
  bool default_bill = 4;

  // How much electricity the house uses in an average month, based on the
  // bill size and the local electricity rates.
  float average_kwh_per_month = 5;

  // Index in [solar_panel_configs]
  // [google.maps.solar.v1.SolarPotential.solar_panel_configs]
  // of the optimum solar layout for this bill size. This can be -1
  // indicating that there is no layout. In this case, the remaining
  // submessages will be omitted.
  optional int32 panel_config_index = 6;

  // Financial information that applies regardless of the financing method
  // used.
  FinancialDetails financial_details = 7;

  // Cost and benefit of leasing the solar panels.
  LeasingSavings leasing_savings = 8;

  // Cost and benefit of buying the solar panels with cash.
  CashPurchaseSavings cash_purchase_savings = 9;

  // Cost and benefit of buying the solar panels by financing the purchase.
  FinancedPurchaseSavings financed_purchase_savings = 10;
}

// Details of a financial analysis. Some of these details are already
// stored at higher levels (e.g., out of pocket cost). Total money
// amounts are over a lifetime period defined by the
// [panel_lifetime_years]
// [google.maps.solar.v1.SolarPotential.panel_lifetime_years]
// field in [SolarPotential]
// [google.maps.solar.v1.SolarPotential]. Note: The out of
// pocket cost of purchasing the panels is given in the
// [out_of_pocket_cost]
// [google.maps.solar.v1.CashPurchaseSavings.out_of_pocket_cost]
// field in [CashPurchaseSavings]
// [google.maps.solar.v1.CashPurchaseSavings].
message FinancialDetails {
  // How many AC kWh we think the solar panels will generate in their first
  // year.
  float initial_ac_kwh_per_year = 1;

  // Utility bill for electricity not produced by solar, for the
  // lifetime of the panels.
  google.type.Money remaining_lifetime_utility_bill = 2;

  // Amount of money available from federal incentives; this applies if the
  // user buys (with or without a loan) the panels.
  google.type.Money federal_incentive = 3;

  // Amount of money available from state incentives; this applies if the
  // user buys (with or without a loan) the panels.
  google.type.Money state_incentive = 4;

  // Amount of money available from utility incentives; this applies if the
  // user buys (with or without a loan) the panels.
  google.type.Money utility_incentive = 5;

  // Amount of money the user will receive from Solar Renewable Energy
  // Credits over the panel lifetime; this applies if the user buys
  // (with or without a loan) the panels.
  google.type.Money lifetime_srec_total = 6;

  // Total cost of electricity the user would have paid over the
  // lifetime period if they didn't install solar.
  google.type.Money cost_of_electricity_without_solar = 7;

  // Whether net metering is allowed.
  bool net_metering_allowed = 8;

  // Percentage (0-100) of the user's power supplied by solar.
  // Valid for the first year but approximately correct for future years.
  optional float solar_percentage = 9;

  // The percentage (0-100) of solar electricity production we assumed was
  // exported to the grid, based on the first quarter of production. This
  // affects the calculations if net metering is not allowed.
  optional float percentage_exported_to_grid = 10;
}

// Financial information that's shared between different financing methods.
message SavingsOverTime {
  // Savings in the first year after panel installation.
  google.type.Money savings_year1 = 1;

  // Savings in the first twenty years after panel installation.
  google.type.Money savings_year20 = 2;

  // Using the assumed discount rate, what is the present value of the
  // cumulative 20-year savings?
  google.type.Money present_value_of_savings_year20 = 3;

  // Savings in the entire panel lifetime.
  google.type.Money savings_lifetime = 5;

  // Using the assumed discount rate, what is the present value of the
  // cumulative lifetime savings?
  google.type.Money present_value_of_savings_lifetime = 6;

  // Indicates whether this scenario is financially viable.  Will be false for
  // scenarios with poor financial viability (e.g., money-losing).
  bool financially_viable = 4;
}

// Cost and benefit of leasing a particular configuration of solar panels
// with a particular electricity usage.
message LeasingSavings {
  // Whether leases are allowed in this juristiction (leases are not
  // allowed in some states). If this field is false, then the values in
  // this message should probably be ignored.
  bool leases_allowed = 1;

  // Whether leases are supported in this juristiction by the financial
  // calculation engine. If this field is false, then the values in this
  // message should probably be ignored. This is independent of
  // `leases_allowed`: in some areas leases are allowed, but under conditions
  // that aren't handled by the financial models.
  bool leases_supported = 2;

  // Estimated annual leasing cost.
  google.type.Money annual_leasing_cost = 3;

  // How much is saved (or not) over the lifetime period.
  SavingsOverTime savings = 4;
}

// Cost and benefit of an outright purchase of a particular configuration
// of solar panels with a particular electricity usage.
message CashPurchaseSavings {
  // Initial cost before tax incentives: the amount that must be paid
  // out-of-pocket. Contrast with `upfront_cost`, which is after tax incentives.
  google.type.Money out_of_pocket_cost = 1;

  // Initial cost after tax incentives: it's the amount that must be paid
  // during first year. Contrast with `out_of_pocket_cost`, which is before tax
  // incentives.
  google.type.Money upfront_cost = 2;

  // The value of all tax rebates.
  google.type.Money rebate_value = 3;

  // Number of years until payback occurs. A negative value means payback
  // never occurs within the lifetime period.
  optional float payback_years = 4;

  // How much is saved (or not) over the lifetime period.
  SavingsOverTime savings = 5;
}

// Cost and benefit of using a loan to buy a particular configuration
// of solar panels with a particular electricity usage.
// Initial out of pocket cost is zero in our model: the loan covers
// everything.
message FinancedPurchaseSavings {
  // Annual loan payments.
  google.type.Money annual_loan_payment = 1;

  // The value of all tax rebates (including Federal Investment Tax Credit
  // (ITC)).
  google.type.Money rebate_value = 2;

  // The interest rate on loans assumed in this set of calculations.
  float loan_interest_rate = 3;

  // How much is saved (or not) over the lifetime period.
  SavingsOverTime savings = 4;
}

// Request message for `Solar.GetDataLayers`.
message GetDataLayersRequest {
  // The longitude and latitude for the center of the region to get data for.
  google.type.LatLng location = 1;

  // The radius, in meters, defining the region surrounding that
  // centre point for which data should be returned. The limitations
  // on this value are:
  //
  // * Any value up to 100m can always be specified.
  // * Values over 100m can be specified, as long as
  //   `radius_meters` <= `pixel_size_meters * 1000`.
  // * However, for values over 175m, the `DataLayerView` in the
  //   request must not include monthly flux or hourly shade.
  float radius_meters = 2;

  // The desired subset of the data to return.
  DataLayerView view = 3;

  // The minimum quality level allowed in the results. No result with
  // lower quality than this will be returned. Not specifying this is
  // equivalent to restricting to HIGH quality only.
  ImageryQuality required_quality = 5;

  // The minimum scale, in meters per pixel, of the data to return.
  // Values of 0.1 (the default, if this field is not set explicitly),
  // 0.25, 0.5, and 1.0 are supported. Imagery components whose normal
  // resolution is less than `pixel_size_meters` will be returned at
  // the resolution specified by `pixel_size_meters`; imagery
  // components whose normal resolution is equal to or greater than
  // `pixel_size_meters` will be returned at that normal resolution.
  float pixel_size_meters = 6;
}

// Information about the solar potential of a region. The actual data
// are contained in a number of GeoTIFF files covering the requested
// region, for which this message contains URLs: Each string in the
// `DataLayers` message contains a URL from which the
// corresponding GeoTIFF can be fetched. These URLs are valid for a
// few hours after they've been generated. Most of the GeoTIFF files
// are at a resolution of 0.1m/pixel, but the monthly flux file is at
// 0.5m/pixel, and the hourly shade files are at 1m/pixel. If a
// `pixel_size_meters` value was specified in the
// `GetDataLayersRequest`, then the minimum resolution in the GeoTIFF
// files will be that value.
message DataLayers {
  // When the source imagery (from which all the other data are derived) in this
  // region was taken. It is necessarily somewhat approximate, as the images may
  // have been taken over more than one day.
  google.type.Date imagery_date = 1;

  // When processing was completed on this imagery.
  google.type.Date imagery_processed_date = 2;

  // The URL for an image of the DSM (Digital Surface Model) of the region.
  // Values are in meters above EGM96 geoid (i.e., sea level). Invalid locations
  // (where we don't have data) are stored as -9999.
  string dsm_url = 3;

  // The URL for an image of RGB data (aerial photo) of the region.
  string rgb_url = 4;

  // The URL for the building mask image: one bit per pixel saying whether that
  // pixel is considered to be part of a rooftop or not.
  string mask_url = 5;

  // The URL for the annual flux map (annual sunlight on roofs) of the region.
  // Values are kWh/kW/year. This is *unmasked flux*: flux is computed for every
  // location, not just building rooftops. Invalid locations are stored as
  // -9999: locations outside our coverage area will be invalid, and a few
  // locations inside the coverage area, where we were unable to calculate flux,
  // will also be invalid.
  string annual_flux_url = 6;

  // The URL for the monthly flux map (sunlight on roofs, broken down by month)
  // of the region. Values are kWh/kW/year. The GeoTIFF pointed to by this URL
  // will contain twelve bands, corresponding to January...December, in order.
  string monthly_flux_url = 7;

  // Twelve URLs for hourly shade, corresponding to January...December, in
  // order. Each GeoTIFF will contain 24 bands, corresponding to the 24 hours of
  // the day. Each pixel is a 32 bit integer, corresponding to the (up to) 31
  // days of that month; a 1 bit means that the corresponding location is able
  // to see the sun at that day, of that hour, of that month. Invalid locations
  // are stored as -9999 (since this is negative, it has bit 31 set, and no
  // valid value could have bit 31 set as that would correspond to the 32nd day
  // of the month).
  //
  // An example may be useful. If you want to know whether a point (at
  // pixel location (x, y)) saw sun at 4pm on the 22nd of June you
  // would:
  //
  // 1. fetch the sixth URL in this list (corresponding to June).
  // 1. look up the 17th channel (corresponding to 4pm).
  // 1. read the 32-bit value at (x, y).
  // 1. read bit 21 of the value (corresponding to the 22nd of the month).
  // 1. if that bit is a 1, then that spot saw the sun at 4pm 22 June.
  //
  // More formally:
  // Given `month` (1-12), `day` (1...month max; February has 28 days)
  // and `hour` (0-23), the shade/sun for that month/day/hour at a
  // position `(x, y)` is the bit
  // ```
  // (hourly_shade[month - 1])(x, y)[hour] & (1 << (day - 1))
  // ```
  // where `(x, y)` is spatial indexing, `[month - 1]` refers to
  // fetching the `month - 1`st URL (indexing from zero), `[hour]` is
  // indexing into the channels, and a final non-zero result means
  // "sunny". There are no leap days, and DST doesn't exist (all days
  // are 24 hours long; noon is always "standard time" noon).
  repeated string hourly_shade_urls = 8;

  // The quality of the result's imagery.
  ImageryQuality imagery_quality = 9;
}

// What subset of the solar information to return.
enum DataLayerView {
  // Equivalent to FULL.
  DATA_LAYER_VIEW_UNSPECIFIED = 0;

  // Get the DSM only.
  DSM_LAYER = 1;

  // Get the DSM, RGB, and mask.
  IMAGERY_LAYERS = 2;

  // Get the DSM, RGB, mask, and annual flux.
  IMAGERY_AND_ANNUAL_FLUX_LAYERS = 3;

  // Get the DSM, RGB, mask, annual flux, and monthly flux.
  IMAGERY_AND_ALL_FLUX_LAYERS = 4;

  // Get all data.
  FULL_LAYERS = 5;
}

// The quality of the imagery used to compute some API result.
//
// Note: Regardless of imagery quality level, DSM outputs always have a
// resolution of 0.1 m/pixel, monthly flux outputs always have a resolution of
// 0.5 m/pixel, and hourly shade outputs always have a resolution of 1 m/pixel.
enum ImageryQuality {
  // No quality is known.
  IMAGERY_QUALITY_UNSPECIFIED = 0;

  // The underlying imagery and DSM data were processed at 0.1 m/pixel.
  HIGH = 1;

  // The underlying imagery and DSM data were processed at 0.25 m/pixel.
  MEDIUM = 2;

  // The underlying imagery and DSM data were processed at 0.5 m/pixel.
  LOW = 3;
}

// The orientation of a solar panel. This must be interpreted relative to the
// azimuth of the roof segment that the panel is placed on.
enum SolarPanelOrientation {
  // No panel orientation is known.
  SOLAR_PANEL_ORIENTATION_UNSPECIFIED = 0;

  // A `LANDSCAPE` panel has its long edge perpendicular to the
  // azimuth direction of the roof segment that it is placed on.
  LANDSCAPE = 1;

  // A `PORTRAIT` panel has its long edge parallel to the azimuth
  // direction of the roof segment that it is placed on.
  PORTRAIT = 2;
}

// Request message for `Solar.GetGeoTiff`.
message GetGeoTiffRequest {
  // The ID of the asset being requested.
  string id = 1;
}
