add shading

This commit is contained in:
woheller69 2023-04-06 10:12:17 +02:00
parent 83cc2dae9d
commit e9cb25471e
14 changed files with 193 additions and 20 deletions

View file

@ -45,6 +45,11 @@ Maximum power of your inverter. If it is lower than the maximum power of your pa
#### Inverter efficiency [%]
Efficiency of your inverter.
#### Shading
In this section you can define the shading on your solar panels.
For each azimuth angle range, you can specify the minimum elevation of the sun that is necessary for the sun to hit the solar panels.
For elevations below this value you can set the percentage of shading. For example, a building will reduce radiation by 100%, a tree maybe only by 60%.
## License
This app is licensed under the GPLv3.

View file

@ -19,8 +19,10 @@ public class SolarPowerPlant {
double inverterEfficiency;
double azimuthAngle;
double tiltAngle;
private int[] shadingElevation;
private int[] shadingOpacity;
public SolarPowerPlant(double latitude, double longitude, double cellsMaxPower, double cellsArea, double cellsEfficiency, double diffuseEfficiency, double inverterPowerLimit, double inverterEfficiency, double azimuthAngle, double tiltAngle) {
public SolarPowerPlant(double latitude, double longitude, double cellsMaxPower, double cellsArea, double cellsEfficiency, double diffuseEfficiency, double inverterPowerLimit, double inverterEfficiency, double azimuthAngle, double tiltAngle, int[] shadingElevation, int[] shadingOpacity ) {
this.latitude = latitude;
this.longitude = longitude;
this.cellsMaxPower = cellsMaxPower;
@ -31,6 +33,8 @@ public class SolarPowerPlant {
this.inverterEfficiency = inverterEfficiency / 100;
this.azimuthAngle = azimuthAngle;
this.tiltAngle = tiltAngle;
this.shadingElevation = shadingElevation;
this.shadingOpacity = shadingOpacity;
}
@ -51,14 +55,21 @@ public class SolarPowerPlant {
Double[] normalPanel = {Math.sin(azimuthAngle / 180 * Math.PI) * Math.cos((90 - tiltAngle) / 180 * Math.PI), Math.cos(azimuthAngle / 180 * Math.PI) * Math.cos((90 - tiltAngle) / 180 * Math.PI), Math.sin((90 - tiltAngle) / 180 * Math.PI)};
double efficiency = 0; //calculate scalar product of sunDirection and normalPanel vectors
for (int j = 0; j < directionSun.length; j++) {
efficiency += directionSun[j] * normalPanel[j];
if(solarPowerNormal>0) { //only needed if normal radiation is available
for (int j = 0; j < directionSun.length; j++) {
efficiency += directionSun[j] * normalPanel[j];
}
efficiency = Math.max(0, efficiency); //scalar product is negative if sun points to back of module. set 0 in this case
if (efficiency > 0) {
//Calculate shading in 10 degree ranges, total 36 ranges
int shadingIndex = ((((int) Math.round((solarAzimuth + 5) / 10)) - 1) % 36 + 36) % 36;
if (shadingElevation[shadingIndex] > solarElevation) {
efficiency *= (double) (100 - shadingOpacity[shadingIndex])/100;
}
}
}
efficiency = Math.max(0,efficiency); //scalar product is negative if sun points to back of module. set 0 in this case
//TODO solarPowerDiffuse ignored so far
double dcPower = (solarPowerNormal * efficiency + solarPowerDiffuse * diffuseEfficiency )* cellsEfficiency * cellsArea;
double acPower = Math.min(dcPower * inverterEfficiency, inverterPowerLimit);

View file

@ -10,11 +10,16 @@ import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.ItemTouchHelper;
import android.text.Editable;
import android.text.InputFilter;
import android.text.InputType;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.woheller69.weather.R;
@ -22,6 +27,7 @@ import org.woheller69.weather.database.City;
import org.woheller69.weather.database.CityToWatch;
import org.woheller69.weather.database.SQLiteHelper;
import org.woheller69.weather.dialogs.AddLocationDialogOmGeocodingAPI;
import org.woheller69.weather.ui.Help.InputFilterMinMax;
import org.woheller69.weather.ui.RecycleList.RecyclerItemClickListener;
import org.woheller69.weather.ui.RecycleList.RecyclerOverviewListAdapter;
import org.woheller69.weather.ui.RecycleList.SimpleItemTouchHelperCallback;
@ -111,11 +117,66 @@ public class ManageLocationsActivity extends NavigationActivity {
}
private void editCityToWatch(CityToWatch city) {
int[] shadingElevation = city.getShadingElevation();
int[] shadingOpacity = city.getShadingOpacity();
TextView[] textViews = new TextView[shadingElevation.length];
EditText[] elevationViews = new EditText[shadingElevation.length];
EditText[] opacityViews = new EditText[shadingElevation.length];
AlertDialog.Builder alert = new AlertDialog.Builder(context);
LayoutInflater inflater = getLayoutInflater();
View dialogView = inflater.inflate(R.layout.dialog_edit_location, null);
ViewGroup shading = (ViewGroup) dialogView.findViewById(R.id.edit_Location_shading);
LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT);
p.weight = 1;
LinearLayout header = new LinearLayout(this);
TextView headerRange = new TextView(this);
TextView headerMinElevation = new TextView(this);
TextView headerOpacity = new TextView(this);
headerRange.setText(R.string.edit_location_shading_azimuth_heading);
headerRange.setPadding(5,0,5,0);
headerMinElevation.setText(R.string.edit_location_shading_solar_elevation_heading);
headerMinElevation.setPadding(5,0,5,0);
headerOpacity.setText(R.string.edit_location_shading_opacity_heading);
headerOpacity.setPadding(5,0,5,0);
headerRange.setLayoutParams(p);
headerMinElevation.setLayoutParams(p);
headerOpacity.setLayoutParams(p);
header.addView(headerRange);
header.addView(headerMinElevation);
header.addView(headerOpacity);
shading.addView(header);
LinearLayout[] container = new LinearLayout[shadingElevation.length];
for (int i = 0; i < shadingElevation.length; i++) {
container[i] = new LinearLayout(this);
container[i].setOrientation(LinearLayout.HORIZONTAL);
textViews[i] = new TextView(this);
opacityViews[i] = new EditText(this);
elevationViews[i] = new EditText(this);
textViews[i].setText("[" + (i*10) +","+ (i*10+10)+"]");
textViews[i].setTextSize(18);
elevationViews[i].setText(Integer.toString(shadingElevation[i]));
elevationViews[i].setInputType(InputType.TYPE_CLASS_NUMBER);
elevationViews[i].setFilters(new InputFilter[]{ new InputFilterMinMax(0, 90)});
elevationViews[i].setTextSize(18);
opacityViews[i].setText(Integer.toString(shadingOpacity[i]));
opacityViews[i].setInputType(InputType.TYPE_CLASS_NUMBER);
opacityViews[i].setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)});
opacityViews[i].setTextSize(18);
textViews[i].setLayoutParams(p);
elevationViews[i].setLayoutParams(p);
opacityViews[i].setLayoutParams(p);
container[i].addView(textViews[i]);
container[i].addView(elevationViews[i]);
container[i].addView(opacityViews[i]);
shading.addView(container[i]);
}
alert.setTitle(getString(R.string.edit_location_title));
alert.setView(dialogView);
EditText editLatitude = (EditText) dialogView.findViewById(R.id.EditLocation_Lat);
@ -132,15 +193,22 @@ public class ManageLocationsActivity extends NavigationActivity {
editCity.setText(city.getCityName());
editLatitude.setText(Float.toString(city.getLatitude()));
editLatitude.setFilters(new InputFilter[]{ new InputFilterMinMax(-90, 90)});
editLongitude.setText(Float.toString(city.getLongitude()));
editLongitude.setFilters(new InputFilter[]{ new InputFilterMinMax(-180, 180)});
editAzimuth.setText(Float.toString(city.getAzimuthAngle()));
editAzimuth.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 360)});
editTilt.setText(Float.toString(city.getTiltAngle()));
editTilt.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 90)});
editCellsMaxPower.setText(Float.toString(city.getCellsMaxPower()));
editCellsArea.setText(Float.toString(city.getCellsArea()));
editCellsEfficiency.setText(Float.toString(city.getCellsEfficiency()));
editCellsEfficiency.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)});
editDiffuseEfficiency.setText(Float.toString(city.getDiffuseEfficiency()));
editDiffuseEfficiency.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)});
editInverterPowerLimit.setText(Float.toString(city.getInverterPowerLimit()));
editInverterEfficiency.setText(Float.toString(city.getInverterEfficiency()));
editInverterEfficiency.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)});
editTilt.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { }
@ -156,6 +224,11 @@ public class ManageLocationsActivity extends NavigationActivity {
});
alert.setPositiveButton(getString(R.string.dialog_edit_change_button), (dialog, whichButton) -> {
for (int i = 0; i < shadingElevation.length ; i++) {
shadingElevation[i]= Integer.parseInt(elevationViews[i].getText().toString().isEmpty() ? "0" : elevationViews[i].getText().toString());
shadingOpacity[i]= Integer.parseInt(opacityViews[i].getText().toString().isEmpty() ? "0" : opacityViews[i].getText().toString());
}
adapter.updateCity(city, String.valueOf(editCity.getText()),
Float.parseFloat(editLatitude.getText().toString().isEmpty() ? "0" : editLatitude.getText().toString()),
Float.parseFloat(editLongitude.getText().toString().isEmpty() ? "0" : editLongitude.getText().toString()),
@ -166,7 +239,9 @@ public class ManageLocationsActivity extends NavigationActivity {
Float.parseFloat(editCellsEfficiency.getText().toString().isEmpty() ? "0" : editCellsEfficiency.getText().toString()),
Float.parseFloat(editDiffuseEfficiency.getText().toString().isEmpty() ? "0" : editDiffuseEfficiency.getText().toString()),
Float.parseFloat(editInverterPowerLimit.getText().toString().isEmpty() ? "0" : editInverterPowerLimit.getText().toString()),
Float.parseFloat(editInverterEfficiency.getText().toString().isEmpty() ? "0" : editInverterEfficiency.getText().toString())
Float.parseFloat(editInverterEfficiency.getText().toString().isEmpty() ? "0" : editInverterEfficiency.getText().toString()),
shadingElevation,
shadingOpacity
);
});
alert.setNegativeButton(getString(R.string.dialog_add_close_button), (dialog, whichButton) -> {
@ -208,4 +283,5 @@ public class ManageLocationsActivity extends NavigationActivity {
selectedCity.getCityName()
);
}
}

View file

@ -24,7 +24,7 @@ public class CityToWatch {
private float tiltAngle;
private int rank;
private int[] shadingElevation = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
private int[] shadingOpacity = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
private int[] shadingOpacity = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
public CityToWatch() {
}

View file

@ -0,0 +1,32 @@
package org.woheller69.weather.ui.Help;
import android.text.InputFilter;
import android.text.Spanned;
public class InputFilterMinMax implements InputFilter {
private int min, max;
public InputFilterMinMax(int min, int max) {
this.min = min;
this.max = max;
}
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
String oldString = dest.toString();
String insertString = source.toString();
String newString = new StringBuilder(oldString).insert(dstart,insertString).toString();
float input = Float.parseFloat(newString);
if (isInRange(min, max, input))
return null;
else
return "";
}
private boolean isInRange(int a, int b, float c) {
return b > a ? c >= a && c <= b : c >= b && c <= a;
}
}

View file

@ -3,8 +3,11 @@ package org.woheller69.weather.ui.RecycleList;
import android.content.Context;
import androidx.core.content.ContextCompat;
import androidx.core.content.res.ResourcesCompat;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.content.SharedPreferences;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -67,12 +70,16 @@ public class CityWeatherAdapter extends RecyclerView.Adapter<CityWeatherAdapter.
// function update 3-hour or 1-hour forecast list
public void updateForecastData(List<HourlyForecast> hourlyForecasts) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
courseDayList = new ArrayList<>();
for (HourlyForecast f : hourlyForecasts) {
if (f.getForecastTime() >= System.currentTimeMillis()) {
if (sp.getBoolean("pref_debug",false)) {
courseDayList.add(f);
} else {
if (f.getForecastTime() >= System.currentTimeMillis()) {
courseDayList.add(f);
}
}
}
notifyDataSetChanged();

View file

@ -98,15 +98,22 @@ public class CourseOfDayAdapter extends RecyclerView.Adapter<CourseOfDayAdapter.
if (sp.getBoolean("pref_debug",false)) {
holder.diffuseRadiation.setVisibility(View.VISIBLE);
holder.directRadiationNormal.setVisibility(View.VISIBLE);
holder.energyCum.setVisibility(View.VISIBLE);
} else {
holder.diffuseRadiation.setVisibility(View.GONE);
holder.directRadiationNormal.setVisibility(View.GONE);
holder.energyCum.setVisibility(View.GONE);
}
holder.time.setText(StringFormatUtils.formatTimeWithoutZone(context, courseOfDayList.get(position).getLocalForecastTime(context)));
holder.directRadiationNormal.setText(StringFormatUtils.formatInt(courseOfDayList.get(position).getDirectRadiationNormal()," W/qm"));
holder.diffuseRadiation.setText(StringFormatUtils.formatInt(courseOfDayList.get(position).getDiffuseRadiation()," W/qm"));
holder.power.setText(StringFormatUtils.formatInt(courseOfDayList.get(position).getPower()," "+ context.getString(R.string.units_Wh)));
float energyCumulated=0;
for (int i=0; i<=position;i++)
energyCumulated+=courseOfDayList.get(i).getPower();
holder.energyCum.setText(StringFormatUtils.formatInt(energyCumulated," "+ context.getString(R.string.units_Wh)));
updateRecyclerViewHeader(); //update header according to date in first visible item on the left
setIcon(courseOfDayList.get(position).getWeatherID(), holder.weather, isDay);
@ -148,7 +155,7 @@ public class CourseOfDayAdapter extends RecyclerView.Adapter<CourseOfDayAdapter.
TextView directRadiationNormal;
TextView diffuseRadiation;
TextView power;
TextView wind_speed;
TextView energyCum;
CourseOfDayViewHolder(View itemView) {
super(itemView);
@ -158,6 +165,7 @@ public class CourseOfDayAdapter extends RecyclerView.Adapter<CourseOfDayAdapter.
directRadiationNormal = itemView.findViewById(R.id.course_of_day_direct);
diffuseRadiation = itemView.findViewById(R.id.course_of_day_diffuse);
power = itemView.findViewById(R.id.course_of_day_power);
energyCum = itemView.findViewById(R.id.course_of_energy_cum);
}
}

View file

@ -114,7 +114,7 @@ public class RecyclerOverviewListAdapter extends RecyclerView.Adapter<ItemViewHo
public CityToWatch getCitytoWatch(int position){
return cities.get(position);
}
public void updateCity(CityToWatch cityToWatch, String cityName, float latitude, float longitude, float azimuth, float tilt, float cellsMaxPower, float cellsArea, float cellsEfficiency, float diffuseEfficiency, float inverterPowerLimit, float inverterEfficiency) {
public void updateCity(CityToWatch cityToWatch, String cityName, float latitude, float longitude, float azimuth, float tilt, float cellsMaxPower, float cellsArea, float cellsEfficiency, float diffuseEfficiency, float inverterPowerLimit, float inverterEfficiency, int[] shadingElevation, int[] shadingOpacity) {
cityToWatch.setCityName(cityName);
cityToWatch.setLatitude(latitude);
cityToWatch.setLongitude(longitude);
@ -126,6 +126,8 @@ public class RecyclerOverviewListAdapter extends RecyclerView.Adapter<ItemViewHo
cityToWatch.setDiffuseEfficiency(diffuseEfficiency);
cityToWatch.setInverterPowerLimit(inverterPowerLimit);
cityToWatch.setInverterEfficiency(inverterEfficiency);
cityToWatch.setShadingElevation(shadingElevation);
cityToWatch.setShadingOpacity(shadingOpacity);
database.updateCityToWatch(cityToWatch);
notifyDataSetChanged();
}

View file

@ -73,7 +73,7 @@ public class OMDataExtractor implements IDataExtractor {
SQLiteHelper dbhelper = SQLiteHelper.getInstance(context);
CityToWatch city = dbhelper.getCityToWatch(cityID);
SolarPowerPlant spp = new SolarPowerPlant(city.getLatitude(), city.getLongitude(), city.getCellsMaxPower(), city.getCellsArea(), city.getCellsEfficiency(),city.getDiffuseEfficiency(), city.getInverterPowerLimit(), city.getInverterEfficiency(), city.getAzimuthAngle(), city.getTiltAngle());
SolarPowerPlant spp = new SolarPowerPlant(city.getLatitude(), city.getLongitude(), city.getCellsMaxPower(), city.getCellsArea(), city.getCellsEfficiency(),city.getDiffuseEfficiency(), city.getInverterPowerLimit(), city.getInverterEfficiency(), city.getAzimuthAngle(), city.getTiltAngle(), city.getShadingElevation(), city.getShadingOpacity());
IApiToDatabaseConversion conversion = new OMToDatabaseConversion();

View file

@ -5,7 +5,7 @@
android:layout_height="wrap_content">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="0dp"
android:paddingLeft="@dimen/activity_horizontal_margin"
@ -121,5 +121,20 @@
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:hint="@string/edit_location_hint_inverter_efficiency"/>
<LinearLayout
android:id="@+id/edit_Location_shading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="10dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/edit_location_shading_heading"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:layout_marginBottom="10dp"/>
</LinearLayout>
</LinearLayout>
</ScrollView>

View file

@ -48,7 +48,16 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:text="xy W"
android:text="xy Wh"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/colorPrimaryDark" />
<TextView
android:id="@+id/course_of_energy_cum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:text="xy Wh"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/colorPrimaryDark" />

View file

@ -96,7 +96,7 @@
<string name="dialog_OK_button">OK</string>
<string name="dialog_NO_button">Nein</string>
<string name="dialog_Later_button">Vielleicht später</string>
<string name="dialog_StarOnGitHub">Mögen Sie diese App? Bitte vergeben Sie einen Stern auf GitHub oder spendieren Sie dem Entwickler einen Kaffee über PayPal.</string>
<string name="dialog_StarOnGitHub">Mögen Sie diese App? Bitte vergeben Sie einen Stern auf GitHub und spendieren Sie dem Entwickler einen Kaffee über PayPal.</string>
<string name="settings_show_pressure">Luftdruck anzeigen</string>
<string name="settings_GPS_manual">Nur manuell aktualisieren</string>
<string name="settings_interval_quarter">15 min</string>
@ -116,7 +116,7 @@
<string name="settings_forecast_days">Anzahl der prognostizierten Tage</string>
<string name="edit_location_hint_latitude">Breitengrad [°]</string>
<string name="edit_location_hint_longitude">Längengrad [°]</string>
<string name="edit_location_hint_azimuth">Azimuth [°]</string>
<string name="edit_location_hint_azimuth">Azimut [°]</string>
<string name="edit_location_hint_tilt">Neigung [°]</string>
<string name="edit_location_hint_cells_max_power">Maximalleistung Zelle [W]</string>
<string name="edit_location_hint_cells_efficiency">Wirkungsgrad Zelle [%]</string>
@ -126,5 +126,9 @@
<string name="edit_location_hint_inverter_efficiency">Effizienz Wechselrichter [%]</string>
<string name="units_Wh">Wh</string>
<string name="edit_location_title">Ort bearbeiten</string>
<string name="edit_location_shading_azimuth_heading">Azimut Bereich [°]</string>
<string name="edit_location_shading_solar_elevation_heading">Min. Elevation der Sonne [°]</string>
<string name="edit_location_shading_opacity_heading">Abschattung unterhalb dieser Elevation [%]</string>
<string name="edit_location_shading_heading">Abschattung</string>
</resources>

View file

@ -104,7 +104,7 @@
<string name="dialog_OK_button">OK</string>
<string name="dialog_NO_button">No</string>
<string name="dialog_Later_button">Maybe later</string>
<string name="dialog_StarOnGitHub">Do you like this app? Please give a star on GitHub or buy the developer a coffee via PayPal.</string>
<string name="dialog_StarOnGitHub">Do you like this app? Please give a star on GitHub and buy the developer a coffee via PayPal.</string>
<string name="settings_show_pressure">Show air pressure</string>
<string name="action_position" translatable="false">Update Location</string>
<string name="settings_GPS_manual">Manual update only</string>
@ -128,4 +128,8 @@
<string name="edit_location_hint_inverter_efficiency">Inverter efficiency [%]</string>
<string name="units_Wh">Wh</string>
<string name="edit_location_title">Edit location</string>
<string name="edit_location_shading_azimuth_heading">Azimuth range [°]</string>
<string name="edit_location_shading_solar_elevation_heading">Min. solar elevation [°]</string>
<string name="edit_location_shading_opacity_heading">Shading below this elevation [%]</string>
<string name="edit_location_shading_heading">Shading</string>
</resources>

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB