diff --git a/README.md b/README.md index 7fb6955..477610e 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/app/src/main/java/org/woheller69/weather/SolarPowerPlant.java b/app/src/main/java/org/woheller69/weather/SolarPowerPlant.java index c90dd57..d4a0d55 100644 --- a/app/src/main/java/org/woheller69/weather/SolarPowerPlant.java +++ b/app/src/main/java/org/woheller69/weather/SolarPowerPlant.java @@ -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); diff --git a/app/src/main/java/org/woheller69/weather/activities/ManageLocationsActivity.java b/app/src/main/java/org/woheller69/weather/activities/ManageLocationsActivity.java index 118d5b7..333df47 100644 --- a/app/src/main/java/org/woheller69/weather/activities/ManageLocationsActivity.java +++ b/app/src/main/java/org/woheller69/weather/activities/ManageLocationsActivity.java @@ -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() ); } + } diff --git a/app/src/main/java/org/woheller69/weather/database/CityToWatch.java b/app/src/main/java/org/woheller69/weather/database/CityToWatch.java index f8742cf..081e2b1 100644 --- a/app/src/main/java/org/woheller69/weather/database/CityToWatch.java +++ b/app/src/main/java/org/woheller69/weather/database/CityToWatch.java @@ -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() { } diff --git a/app/src/main/java/org/woheller69/weather/ui/Help/InputFilterMinMax.java b/app/src/main/java/org/woheller69/weather/ui/Help/InputFilterMinMax.java new file mode 100644 index 0000000..029afa8 --- /dev/null +++ b/app/src/main/java/org/woheller69/weather/ui/Help/InputFilterMinMax.java @@ -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; + } + +} diff --git a/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java b/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java index 91c42b4..03b16eb 100644 --- a/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java +++ b/app/src/main/java/org/woheller69/weather/ui/RecycleList/CityWeatherAdapter.java @@ -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 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(); diff --git a/app/src/main/java/org/woheller69/weather/ui/RecycleList/CourseOfDayAdapter.java b/app/src/main/java/org/woheller69/weather/ui/RecycleList/CourseOfDayAdapter.java index 7acbd77..269cd0d 100644 --- a/app/src/main/java/org/woheller69/weather/ui/RecycleList/CourseOfDayAdapter.java +++ b/app/src/main/java/org/woheller69/weather/ui/RecycleList/CourseOfDayAdapter.java @@ -98,15 +98,22 @@ public class CourseOfDayAdapter extends RecyclerView.Adapter + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_course_of_day.xml b/app/src/main/res/layout/list_item_course_of_day.xml index 734d29e..2a84360 100644 --- a/app/src/main/res/layout/list_item_course_of_day.xml +++ b/app/src/main/res/layout/list_item_course_of_day.xml @@ -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" /> + + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index e985cc7..436376d 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -96,7 +96,7 @@ OK Nein Vielleicht später - Mögen Sie diese App? Bitte vergeben Sie einen Stern auf GitHub oder spendieren Sie dem Entwickler einen Kaffee über PayPal. + Mögen Sie diese App? Bitte vergeben Sie einen Stern auf GitHub und spendieren Sie dem Entwickler einen Kaffee über PayPal. Luftdruck anzeigen Nur manuell aktualisieren 15 min @@ -116,7 +116,7 @@ Anzahl der prognostizierten Tage Breitengrad [°] Längengrad [°] - Azimuth [°] + Azimut [°] Neigung [°] Maximalleistung Zelle [W] Wirkungsgrad Zelle [%] @@ -126,5 +126,9 @@ Effizienz Wechselrichter [%] Wh Ort bearbeiten + Azimut Bereich [°] + Min. Elevation der Sonne [°] + Abschattung unterhalb dieser Elevation [%] + Abschattung diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 60d0e1f..4c6961f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -104,7 +104,7 @@ OK No Maybe later - Do you like this app? Please give a star on GitHub or buy the developer a coffee via PayPal. + Do you like this app? Please give a star on GitHub and buy the developer a coffee via PayPal. Show air pressure Update Location Manual update only @@ -128,4 +128,8 @@ Inverter efficiency [%] Wh Edit location + Azimuth range [°] + Min. solar elevation [°] + Shading below this elevation [%] + Shading diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/04.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/04.png new file mode 100644 index 0000000..7c82f6a Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/04.png differ