aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorBruno Perles2015-12-16 10:02:29 +0100
committerBruno Perles2015-12-16 10:02:29 +0100
commit013f4fa8fe9bb08f3ed1d15f905ca2a8437d6aa7 (patch)
tree426b9c17167c10547da2222517cbd4433ae554fe /app
parent2590606c5912a85b8cb1aaa40c57dab67d75e7f7 (diff)
downloadchouette-core-013f4fa8fe9bb08f3ed1d15f905ca2a8437d6aa7.tar.bz2
Add route_sections for traces
Diffstat (limited to 'app')
-rw-r--r--app/assets/images/icons/edit-disabled.pngbin0 -> 333 bytes
-rw-r--r--app/assets/javascripts/route_section.js.coffee58
-rw-r--r--app/assets/stylesheets/main/route_sections.css.scss68
-rw-r--r--app/assets/stylesheets/main/routes.scss24
-rw-r--r--app/assets/stylesheets/modules/icons.scss5
-rw-r--r--app/controllers/route_sections_controller.rb70
-rw-r--r--app/controllers/route_sections_selectors_controller.rb42
-rw-r--r--app/controllers/routes_controller.rb2
-rw-r--r--app/controllers/stop_areas_controller.rb2
-rw-r--r--app/helpers/breadcrumb_helper.rb11
-rw-r--r--app/helpers/route_section_selectors_helper.rb23
-rw-r--r--app/maps/design/route_section_selector_style_map.rb73
-rw-r--r--app/maps/design/route_section_style_map.rb66
-rw-r--r--app/maps/route_section_map.rb67
-rw-r--r--app/maps/route_section_selector_map.rb118
-rw-r--r--app/models/referential.rb4
-rw-r--r--app/models/route_section_search.rb34
-rw-r--r--app/models/route_sections_selector.rb99
-rw-r--r--app/views/journey_patterns/show.html.erb4
-rw-r--r--app/views/referentials/_counts.html.erb4
-rw-r--r--app/views/route_sections/_form.html.erb24
-rw-r--r--app/views/route_sections/edit.html.erb4
-rw-r--r--app/views/route_sections/index.html.erb56
-rw-r--r--app/views/route_sections/new.html.erb3
-rw-r--r--app/views/route_sections/show.html.erb28
-rw-r--r--app/views/route_sections/show.kml.erb8
-rw-r--r--app/views/route_sections_selectors/_selection.js.erb28
-rw-r--r--app/views/route_sections_selectors/edit.html.erb48
-rw-r--r--app/views/shared/_header.erb4
29 files changed, 964 insertions, 13 deletions
diff --git a/app/assets/images/icons/edit-disabled.png b/app/assets/images/icons/edit-disabled.png
new file mode 100644
index 000000000..aaefed65c
--- /dev/null
+++ b/app/assets/images/icons/edit-disabled.png
Binary files differ
diff --git a/app/assets/javascripts/route_section.js.coffee b/app/assets/javascripts/route_section.js.coffee
new file mode 100644
index 000000000..f2e494c0c
--- /dev/null
+++ b/app/assets/javascripts/route_section.js.coffee
@@ -0,0 +1,58 @@
+class @RouteSectionMap
+ @onSelectedFeature: (feature) ->
+ route_section_id = feature.data.id
+
+ routeSectionOption = $("option[value=#{route_section_id}]")
+ routeSectionOption.parent().val route_section_id
+
+ $('#map-selection').show()
+ $('#empty-map-selection').hide()
+
+ selectionUrl = location.pathname.replace /edit$/, "selection"
+ $.ajax(url: selectionUrl, method: 'POST', data: { route_section_id: route_section_id }, dataType: 'html').done (data) ->
+ $('#map-selection div').replaceWith(data)
+
+ @onUnselectedFeature: (feature) ->
+ $('#map-selection').hide()
+ $('#empty-map-selection').show()
+
+jQuery ->
+ if $("#map.route_section").length > 0 and user_geometry?
+ projWGS84 = new OpenLayers.Projection("EPSG:4326")
+ proj900913 = new OpenLayers.Projection("EPSG:900913")
+ wtk_format = new OpenLayers.Format.WKT()
+
+ user_geometry.events.on({
+ afterfeaturemodified: (event) ->
+ wgs84_geometry = event.feature.geometry.transform(proj900913, projWGS84)
+ wgs84_feature = new OpenLayers.Feature.Vector(wgs84_geometry)
+ ewtk = "SRID=4326;#{wtk_format.write(wgs84_feature)}"
+
+ $('#route_section_editable_geometry').val(ewtk)
+ return
+ })
+
+ $('#new_route_sections_selector select').on 'change', ->
+ new_route_section_id = $(this).val()
+
+ edit_link = $(this).closest("tr").find("a.edit-route-section")
+
+ # Save edit link to play with it
+ unless edit_link.data("href-pattern")?
+ edit_link.data "href-pattern", edit_link.attr('href').replace(new RegExp("/route_sections/([0-9]+)/edit"), "/route_sections/:id/edit")
+
+ if !!new_route_section_id
+ edit_link.removeClass "disabled"
+ edit_link.attr 'href', edit_link.data("href-pattern").replace(/:id/, new_route_section_id)
+ else
+ edit_link.addClass "disabled"
+ edit_link.attr 'href', '#'
+
+ $('form.route_section').find('button[type="submit"]').on 'click', (e) ->
+ e.preventDefault();
+ if typeof modify_feature != 'undefined'
+ modify_feature.deactivate()
+ $('form.route_section').submit()
+ return
+
+ return
diff --git a/app/assets/stylesheets/main/route_sections.css.scss b/app/assets/stylesheets/main/route_sections.css.scss
new file mode 100644
index 000000000..cc1c85abc
--- /dev/null
+++ b/app/assets/stylesheets/main/route_sections.css.scss
@@ -0,0 +1,68 @@
+#workspace.route_sections_selectors.edit {
+ td.route_section {
+ .input {
+ padding: 0;
+ margin: 0;
+
+ select {
+ width: 100%;
+ }
+ }
+ }
+
+ a.edit-route-section {
+ background: url(image-path('icons/edit.png')) no-repeat 0% 50%;
+ text-indent: -9999px;
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+
+ margin-right: 8px;
+
+ &.disabled {
+ background-image: url(image-path('icons/edit-disabled.png'));
+ }
+ }
+
+ #map-selection {
+ position: relative;
+ span {
+ display: inline-block;
+ }
+ .departure, .arrival {
+ width: 25%;
+ }
+ .actions {
+ width: 25%;
+ overflow: visible;
+ padding-left: 0;
+ position: absolute;
+ right: 0;
+ }
+ }
+}
+
+#workspace.route_sections.index {
+ th.distance, th.points {
+ text-align: center;
+ }
+
+ td.distance, td.points, td.actions {
+ text-align: center;
+ }
+}
+
+#workspace.route_sections.edit {
+ .formtastic {
+ .input .label {
+ width: 40%;
+ }
+ .actions {
+ padding-left: 19%;
+ }
+ }
+ #map {
+ width: 600px;
+ heigth: 600px;
+ }
+}
diff --git a/app/assets/stylesheets/main/routes.scss b/app/assets/stylesheets/main/routes.scss
index 7714c13a3..44703c7e5 100644
--- a/app/assets/stylesheets/main/routes.scss
+++ b/app/assets/stylesheets/main/routes.scss
@@ -3,7 +3,7 @@
// You can use Sass (SCSS) here: http://sass-lang.com/
#workspace.lines.show
-{
+{
}
#workspace.routes.edit, #workspace.routes.new, #workspace.routes.create, #workspace.routes.update
@@ -11,13 +11,13 @@
#route_color{ width: 100px;
color: white;
font-weight: bold;}
-
+
#stop_points .nested-fields {
ol {
margin-left: 25%;
- .handle { margin-left: 5px;}
+ .handle { margin-left: 5px;}
- .search_stop_area {
+ .search_stop_area {
margin-bottom: 0px !important;
}
@@ -31,7 +31,7 @@
#stop_points .links {
margin: 10px 0 15px 25%;
- }
+ }
}
#workspace.routes.edit_boarding_alighting{
@@ -39,10 +39,18 @@
.stop_area{
padding-top: 7px;
}
-
+
}
#workspace.routes.show
-{
-}
+{
+}
+.large-map {
+ width: 100%;
+ height: 600px;
+ #map {
+ width: 100%;
+ height: 100%;
+ }
+}
diff --git a/app/assets/stylesheets/modules/icons.scss b/app/assets/stylesheets/modules/icons.scss
index 001e511d1..3c42c3bd6 100644
--- a/app/assets/stylesheets/modules/icons.scss
+++ b/app/assets/stylesheets/modules/icons.scss
@@ -5,6 +5,11 @@ a:before {
vertical-align: middle;
}
+a.view:before { // fa-eye
+ content: "\f06e";
+ color: $brand-success;
+}
+
a.add:before { // fa-plus-circle
content: "\f055";
color: $brand-success;
diff --git a/app/controllers/route_sections_controller.rb b/app/controllers/route_sections_controller.rb
new file mode 100644
index 000000000..64cb8c65c
--- /dev/null
+++ b/app/controllers/route_sections_controller.rb
@@ -0,0 +1,70 @@
+class RouteSectionsController < ChouetteController
+
+ defaults :resource_class => Chouette::RouteSection
+
+ respond_to :html
+ respond_to :kml, :only => :show
+
+ belongs_to :referential
+
+ before_action :save_return_to_path, only: [:edit, :create_to_edit]
+ before_action ->(controller) { build_breadcrumb controller.action_name }
+
+ helper_method :search
+
+ def index
+ index!
+ end
+
+ def new
+ @stop_areas = referential.stop_areas.with_geometry.order :name
+ new!
+ end
+
+ def show
+ @map = RouteSectionMap.new(resource).with_helpers(self)
+ show!
+ end
+
+ def edit
+ @map = RouteSectionMap.new(resource, true).with_helpers(self)
+ edit!
+ end
+
+ def update
+ update! { session.delete(:return_to) }
+ end
+
+ def create
+ create! { session.delete(:return_to) }
+ end
+
+ def create_to_edit
+ route_section = Chouette::RouteSection.create(route_section_params)
+ redirect_to edit_referential_route_section_path(referential, route_section)
+ end
+
+ protected
+
+ def save_return_to_path
+ session[:return_to] = params[:return_to] if params[:return_to]
+ end
+
+ def collection
+ # if q = params[:q]
+ # @route_sections ||= Chouette::RouteSection.joins(:departure, :arrival).where(departure: {name: "#{q}"}).or.where(arrival: {name: "#{q}"})
+ # end
+ @route_sections ||= search.collection.includes(:departure, :arrival).paginate page: params[:page]
+ end
+
+ def search
+ @search ||= RouteSectionSearch.new(params[:route_section_search])
+ end
+
+ private
+
+ def route_section_params
+ params.require(:route_section).permit(:departure_id, :arrival_id, :editable_geometry, :no_processing)
+ end
+
+end
diff --git a/app/controllers/route_sections_selectors_controller.rb b/app/controllers/route_sections_selectors_controller.rb
new file mode 100644
index 000000000..05a399ae8
--- /dev/null
+++ b/app/controllers/route_sections_selectors_controller.rb
@@ -0,0 +1,42 @@
+class RouteSectionsSelectorsController < ChouetteController
+
+ # singleton option makes association_chain crazy
+ #defaults singleton: true
+
+ respond_to :html, only: [ :edit, :update ]
+ respond_to :js, only: :section
+
+ belongs_to :referential do
+ belongs_to :line, :parent_class => Chouette::Line do
+ belongs_to :route, :parent_class => Chouette::Route do
+ belongs_to :journey_pattern, parent_class: Chouette::JourneyPattern
+ end
+ end
+ end
+
+ def edit
+ @map = RouteSectionSelectorMap.new(resource).with_helpers(self)
+ end
+
+ def selection
+ parent
+
+ @route_section = referential.route_sections.find params[:route_section_id].to_i
+ render partial: 'selection', format: 'js'
+ end
+
+ private
+
+ def resource
+ @route_sections_selector ||= RouteSectionsSelector.new parent
+ end
+
+ def build_resource
+ @route_sections_selector ||= RouteSectionsSelector.new parent, *resource_params
+ end
+
+ def route_section_selector_params
+ params.require(:route_section_selector).permit()
+ end
+
+end
diff --git a/app/controllers/routes_controller.rb b/app/controllers/routes_controller.rb
index 6dfaf3dd0..59c129867 100644
--- a/app/controllers/routes_controller.rb
+++ b/app/controllers/routes_controller.rb
@@ -75,7 +75,7 @@ class RoutesController < ChouetteController
end
private
-
+
def route_params
params.require(:route).permit( :direction_code, :wayback_code, :line_id, :objectid, :object_version, :creation_time, :creator_id, :name, :comment, :opposite_route_id, :published_name, :number, :direction, :wayback, { stop_points_attributes: [ :id, :_destroy, :position, :stop_area_id, :for_boarding, :for_alighting ] } )
end
diff --git a/app/controllers/stop_areas_controller.rb b/app/controllers/stop_areas_controller.rb
index 9321ac9ae..dbbadb80c 100644
--- a/app/controllers/stop_areas_controller.rb
+++ b/app/controllers/stop_areas_controller.rb
@@ -137,6 +137,6 @@ class StopAreasController < ChouetteController
def stop_area_params
params.require(:stop_area).permit( :routing_stop_ids, :routing_line_ids, :children_ids, :stop_area_type, :parent_id, :objectid, :object_version, :creation_time, :creator_id, :name, :comment, :area_type, :registration_number, :nearest_topic_name, :fare_code, :longitude, :latitude, :long_lat_type, :country_code, :street_name, :zip_code, :city_name, :mobility_restricted_suitability, :stairs_availability, :lift_availability, :int_user_needs, :coordinates, :url, :time_zone )
- end
+ end
end
diff --git a/app/helpers/breadcrumb_helper.rb b/app/helpers/breadcrumb_helper.rb
index 52da8b887..69494321b 100644
--- a/app/helpers/breadcrumb_helper.rb
+++ b/app/helpers/breadcrumb_helper.rb
@@ -1,6 +1,7 @@
module BreadcrumbHelper
- def build_breadcrumb(action)
+ def build_breadcrumb(action)
+ action = action.to_sym
case resource_class.to_s
when "Chouette::Network"
network_breadcrumb action
@@ -30,6 +31,8 @@ module BreadcrumbHelper
connection_link_breadcrumb action
when "Chouette::TimeTable"
time_table_breadcrumb action
+ when "Chouette::RouteSection"
+ route_section_breadcrumb action
when "Chouette::Timeband"
timeband_breadcrumb action
when "StopAreaCopy"
@@ -106,6 +109,12 @@ module BreadcrumbHelper
add_breadcrumb breadcrumb_label(@time_table), referential_time_table_path(@referential, @time_table),:title => breadcrumb_tooltip(@time_table) if action == :edit
end
+ def route_section_breadcrumb(action)
+ referential_breadcrumb
+ add_breadcrumb Chouette::RouteSection.model_name.human.pluralize, referential_route_sections_path(@referential)
+ add_breadcrumb breadcrumb_label(resource), referential_route_section_path(@referential, resource),:title => breadcrumb_tooltip(resource) if action.in?([:show, :edit])
+ end
+
def timeband_breadcrumb(action)
referential_breadcrumb
add_breadcrumb Chouette::Timeband.model_name.human(:count => 2), referential_timebands_path(@referential) unless action == :index
diff --git a/app/helpers/route_section_selectors_helper.rb b/app/helpers/route_section_selectors_helper.rb
new file mode 100644
index 000000000..1be30066b
--- /dev/null
+++ b/app/helpers/route_section_selectors_helper.rb
@@ -0,0 +1,23 @@
+module RouteSectionSelectorsHelper
+
+ def link_to_edit_route_section(route_section)
+ classes = [ 'edit-route-section' ]
+ link ='#'
+
+ if route_section.present?
+ return_to = edit_referential_line_route_journey_pattern_route_sections_selector_path(@referential, @line, @route, @journey_pattern)
+ link = edit_referential_route_section_path(@referential, route_section, return_to: return_to)
+ else
+ classes << 'disabled'
+ end
+
+ link_to "Edit", link, class: classes, title: t('route_sections_selectors.edit.route_section.edit')
+ end
+
+ def link_to_create_route_section(departure, arrival)
+ return_to = edit_referential_line_route_journey_pattern_route_sections_selector_path(@referential, @line, @route, @journey_pattern)
+ link_to t('route_sections_selectors.edit.route_section.new'),
+ create_to_edit_referential_route_sections_path(@referential, route_section: {departure_id:departure.id, arrival_id: arrival.id}, return_to: return_to)
+ end
+
+end
diff --git a/app/maps/design/route_section_selector_style_map.rb b/app/maps/design/route_section_selector_style_map.rb
new file mode 100644
index 000000000..12f192ec8
--- /dev/null
+++ b/app/maps/design/route_section_selector_style_map.rb
@@ -0,0 +1,73 @@
+class Design::RouteSectionSelectorStyleMap < Design::GenericStyleMap
+ attr_accessor :style
+
+ def initialize(helpers, options = {})
+ @helpers = helpers
+ @style = options[:style].present? ? default_style.merge(options[:style]) : default_style
+ end
+
+ def select_style
+ {
+ fillColor: "blue",
+ graphicName: "square",
+ rotation: 90,
+ strokeColor: "#00dd00",
+ pointRadius: 15,
+ graphicZIndex: 100
+ }
+ end
+
+ def highlight_style
+ {
+ fillColor: "orange",
+ graphicName: "square",
+ rotation: 90,
+ strokeColor: "#dd0000",
+ pointRadius: 15,
+ graphicZIndex: 200
+ }
+ end
+
+ def default_style
+ {
+ label: "${label}",
+ fontColor: "black",
+ fontSize: "11px",
+ fontWeight: "bold",
+ labelAlign: "ct",
+ labelXOffset: 0,
+ labelYOffset: -15,
+ strokeColor: "#0000dd",
+ strokeOpacity: 1,
+ strokeWidth: 3,
+ strokeLineCap: "round",
+ strokeDashstyle: "solid",
+ externalGraphic: @helpers.assets_path_patch( "map/${positionType}.png"),
+ graphicWidth: 12,
+ graphicHeight: 12,
+ graphicOpacity: 1,
+ graphicXOffset: -6,
+ graphicYOffset: -6,
+ display: true,
+ fillColor: "red",
+ graphicName: "square",
+ rotation: 90,
+ graphicZIndex: 10
+ }
+ end
+
+ def context
+ {
+ label: :" function(feature) {if(feature.layer.map.getZoom() > 13) { return feature.attributes.name;} else {return '';}} ",
+ positionType: :" function(feature) { if (feature.attributes.iconCode != undefined) {return feature.attributes.iconCode;} else { return '';} } "
+ }
+ end
+
+ def style_map
+ OpenLayers::StyleMap.new(
+ default: OpenLayers::Style.new(style, { context: context}),
+ select: OpenLayers::Style.new(style.merge(select_style), { context: context }),
+ highlight: OpenLayers::Style.new(style.merge( highlight_style), { context: context }))
+ end
+
+end
diff --git a/app/maps/design/route_section_style_map.rb b/app/maps/design/route_section_style_map.rb
new file mode 100644
index 000000000..2c94bb3a0
--- /dev/null
+++ b/app/maps/design/route_section_style_map.rb
@@ -0,0 +1,66 @@
+class Design::RouteSectionStyleMap < Design::GenericStyleMap
+ attr_accessor :style
+
+ def initialize(helpers, options = {})
+ @helpers= helpers
+ @style = options[:style].present? ? default_style.merge(options[:style]) : default_style
+ end
+
+ def select_style
+ {
+ fillColor:"blue",
+ graphicName:"circle",
+ rotation:90,
+ :strokeColor => "red",
+ pointerEvents: "visiblePainted"
+ }
+ end
+
+ def highlight_style
+ {
+ fillColor:"lightblue",
+ graphicName:"circle",
+ rotation:90,
+ :strokeColor => "#dd0000"
+ }
+ end
+
+ def default_style
+
+ {
+ :fontColor => "black",
+ :fontSize => "11px",
+ :fontWeight => "bold",
+ :labelAlign => "cm",
+ :labelXOffset => 0,
+ :labelYOffset => -15,
+ :strokeColor => "green",
+ :strokeOpacity => 1,
+ :strokeWidth => 2,
+ :strokeLineCap => "round",
+ :strokeLineJoin => "round",
+ :strokeDashstyle => "solid",
+ :lineCap => "round",
+ :lineJoin => "round",
+ :dashstyle => "solid",
+ :graphicWidth => 12,
+ :graphicHeight => 12,
+ :graphicOpacity => 1,
+ :graphicXOffset => -6,
+ :graphicYOffset => -6,
+ :display => true,
+ fillOpacity: 0.5,
+ fillColor: "lightblue",
+ graphicName: "circle",
+ pointRadius: 6,
+ pointerEvents: "visiblePainted",
+ rotation: 90
+ }
+
+ end
+
+ def style_map
+ OpenLayers::StyleMap.new(:default => OpenLayers::Style.new(style), :select => OpenLayers::Style.new(style.merge( select_style)), :highlight => OpenLayers::Style.new(style.merge( highlight_style)))
+ end
+
+end
diff --git a/app/maps/route_section_map.rb b/app/maps/route_section_map.rb
new file mode 100644
index 000000000..cfa40d481
--- /dev/null
+++ b/app/maps/route_section_map.rb
@@ -0,0 +1,67 @@
+class RouteSectionMap < ApplicationMap
+
+ attr_reader :route_section
+
+ attr_accessor :editable
+ alias_method :editable?, :editable
+
+ def initialize(route_section, editable = false)
+ @route_section = route_section
+ @editable = editable
+ end
+
+ def customize_map(map, page)
+ # layers order seems to matter for ModifyFeature control
+ route_section.stop_areas.each do |stop_area|
+ layer = "stop_area_#{stop_area.id}".to_sym
+ page.assign layer.to_s, kml_layer(stop_area, :styleMap => Design::StopAreasStyleMap.new(helpers).style_map)
+ page << map.add_layer(layer)
+ page << map.add_control( hover_control_display_name(layer) )
+ end
+
+ geometry_options = {}.tap do |options|
+ options[:mode] = :editable if editable?
+ end
+ geometry_uneditable_kml_layer = kml_layer(route_section, :styleMap => Design::RouteSectionSelectorStyleMap.new(helpers).style_map)
+ page << map.add_layer(geometry_uneditable_kml_layer)
+
+ if route_section.input_geometry
+ geometry_editable_layer = kml_layer(route_section, geometry_options, {}) # , :styleMap => Design::LineStyleMap.new(style: nil).style_map)
+ else
+ points = route_section.stop_areas.map{|point| OpenLayers::Geometry::Point.new(point.longitude, point.latitude).transform("EPSG:4326", "EPSG:900913")}
+ geometry_editable_layer = OpenLayers::Layer::Vector.new("user_geometry", {:projection => projection("EPSG:4326"), :styleMap => Design::RouteSectionStyleMap.new(helpers).style_map})
+ #geometry_editable_layer = OpenLayers::Layer::Vector.new("user_geometry", {:projection => projection("EPSG:4326")})
+ geometry_editable_features = OpenLayers::Feature::Vector.new(OpenLayers::Geometry::LineString.new(points))
+ page.assign :geometry_editable_features, geometry_editable_features
+ end
+
+ if editable
+ page.assign :user_geometry, geometry_editable_layer
+
+ page << "user_geometry.addFeatures([geometry_editable_features])" if geometry_editable_features
+
+ page << map.add_layer(:user_geometry)
+
+ page.assign :modify_feature, OpenLayers::Control::ModifyFeature.new(:user_geometry, autoActivate: true)
+ page << map.add_control( :modify_feature )
+ else
+ page << map.add_layer(geometry_editable_layer)
+ end
+
+ page << map.zoom_to_extent(bounds.to_google.to_openlayers) if bounds
+ end
+
+ def bounds
+ @bounds ||=
+ if route_section.geometry.present?
+ route_section.geometry.bounds
+ elsif route_section.stop_areas.present?
+ GeoRuby::SimpleFeatures::Point.bounds route_section.stop_areas.collect(&:geometry)
+ end
+ end
+
+ def ready?
+ bounds.present?
+ end
+
+end
diff --git a/app/maps/route_section_selector_map.rb b/app/maps/route_section_selector_map.rb
new file mode 100644
index 000000000..425a6750a
--- /dev/null
+++ b/app/maps/route_section_selector_map.rb
@@ -0,0 +1,118 @@
+class RouteSectionSelectorMap < ApplicationMap
+
+ attr_reader :route_section_selector, :style
+
+ def initialize(route_section_selector, style = nil)
+ @route_section_selector = route_section_selector
+ @style = nil
+ end
+
+ def customize_map(map, page)
+ layer = kml_layer([route_section_selector.itinerary.referential, route_section_selector.itinerary.route.line, route_section_selector.itinerary.route, route_section_selector.itinerary],
+ { rendererOptions: { zIndexing: true }, styleMap: Design::JourneyPatternStyleMap.new(helpers).style_map })
+ page.assign 'journeyPatternLayer', layer
+
+ page << map.add_layer(:journeyPatternLayer)
+ page << map.add_control(hover_control_display_name(:journeyPatternLayer))
+
+ route_section_geometry = OpenLayers::Layer::Vector.new('Route Section Geometry',
+ { projection: projection('EPSG:900913'),
+ rendererOptions: { zIndexing: true },
+ styleMap: Design::RouteSectionSelectorStyleMap.new(helpers).style_map})
+
+ route_section_layer_points = []
+ route_section_selector.sections.reject{|s| s.candidates.length==0}.each do |section|
+ section.candidates.each do |candidate|
+ geometry = candidate.processed_geometry
+ route_section_layer_points << ["#{candidate.id}"] + clean_route_section_line(geometry)
+ end
+ end
+
+ page.assign :route_section_layer_points, route_section_layer_points
+ page << <<EOF
+ var route_section_layers;
+ route_section_layers = route_section_layer_points.map(function(elt, index) {
+ var route_section_id = elt[0];
+ elt.splice(0, 1);
+ var points = elt.map( function(e,i) {
+ return OpenLayers.Projection.transform(new OpenLayers.Geometry.Point(e[0], e[1]), "EPSG:4326", "EPSG:900913" );
+ })
+ return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(points), {id: route_section_id, name: ""});
+ })
+EOF
+ page.assign :route_section_geometry, route_section_geometry
+ page << 'route_section_geometry.addFeatures(route_section_layers)'
+ page << map.add_layer(:route_section_geometry)
+
+ page << <<EOF
+ function selected(feature) {
+ RouteSectionMap.onSelectedFeature(feature);
+ }
+ function unselected(feature) {
+ RouteSectionMap.onUnselectedFeature(feature);
+ }
+
+ highlightControl = new OpenLayers.Control.SelectFeature([route_section_geometry],
+ {
+ clickout: true,
+ toggle: false,
+ multiple:false,
+ hover:true,
+ highlightOnly:true,
+ eventListeners:{
+ featurehighlighted: function (event) {
+ event.feature.layer.drawFeature(
+ event.feature,
+ 'highlight'
+ );
+ },
+ featureunhighlighted: function (event) {
+ event.feature.layer.drawFeature(
+ event.feature,
+ 'default'
+ );
+ }
+ }
+ }
+ );
+ selectControl = new OpenLayers.Control.SelectFeature([route_section_geometry],
+ {
+ onSelect:selected,
+ onUnselect:unselected
+ }
+ );
+ map.addControl(highlightControl);
+ map.addControl(selectControl);
+ highlightControl.activate();
+ selectControl.activate();
+EOF
+
+ #page.assign :select_feature, OpenLayers::Control::SelectFeature.new(:route_section_geometry, {onSelect: selected, onUnselect: unselected})
+ #page << map.add_control( :select_feature )
+
+
+ page << map.zoom_to_extent(bounds.to_google.to_openlayers) if bounds
+
+ end
+
+ def clean_route_section_line(line)
+ point_array = line.to_s.scan(/\d+[.]\d+\s\d+[.]\d+/)
+ proj = OpenLayers::Projection.new('proj')
+
+ point_array.map do |point|
+ point = point.scan(/\d+[.]\d+/)
+ lat = point[0].to_f
+ lng = point[1].to_f
+ [lat, lng]
+ end
+
+ end
+
+ def ready?
+ bounds.present?
+ end
+
+ def bounds
+ @bounds ||= GeoRuby::SimpleFeatures::Point.bounds(route_section_selector.itinerary.route.stop_areas.collect(&:geometry).compact)
+ end
+end
diff --git a/app/models/referential.rb b/app/models/referential.rb
index e32c956fb..d537246e5 100644
--- a/app/models/referential.rb
+++ b/app/models/referential.rb
@@ -91,6 +91,10 @@ class Referential < ActiveRecord::Base
Chouette::VehicleJourney.all
end
+ def route_sections
+ Chouette::RouteSection.all
+ end
+
after_initialize :define_default_attributes
def define_default_attributes
diff --git a/app/models/route_section_search.rb b/app/models/route_section_search.rb
new file mode 100644
index 000000000..a03eb894d
--- /dev/null
+++ b/app/models/route_section_search.rb
@@ -0,0 +1,34 @@
+class RouteSectionSearch
+ include ActiveModel::Conversion
+ extend ActiveModel::Naming
+
+ attr_accessor :departure_name, :arrival_name, :line_id
+ attr_accessor :scope
+
+ def scope
+ scope ||= Chouette::RouteSection
+ end
+
+ def initialize(attributes = {})
+ attributes.each { |k,v| send "#{k}=", v } if attributes
+ end
+
+ def collection()
+ Rails.logger.debug "Search RouteSections with #{inspect}"
+ collection = scope
+
+ [:departure, :arrival].each do |endpoint|
+ endpoint_name = send "#{endpoint}_name"
+ collection = collection.by_endpoint_name(endpoint, endpoint_name) if endpoint_name.present?
+ end
+
+ collection = collection.by_line_id(line_id) if line_id.present?
+
+ collection
+ end
+
+ def persisted?
+ false
+ end
+
+end
diff --git a/app/models/route_sections_selector.rb b/app/models/route_sections_selector.rb
new file mode 100644
index 000000000..ba8a2cc9a
--- /dev/null
+++ b/app/models/route_sections_selector.rb
@@ -0,0 +1,99 @@
+class RouteSectionsSelector
+ extend ActiveModel::Naming
+ include ActiveModel::Conversion
+
+ include ActiveModel::Validations
+
+ attr_reader :itinerary
+
+ def initialize(route_or_journey_pattern, attributes = {})
+ @itinerary = route_or_journey_pattern
+
+ self.attributes = attributes
+ end
+
+ def attributes=(attributes)
+ attributes.each { |k,v| send "#{k}=", v }
+ end
+
+ def update_attributes(attributes)
+ self.attributes = attributes
+ save
+ end
+
+ # def persisted?
+ # false
+ # end
+
+ delegate :stop_points, to: :itinerary
+
+ def route_sections
+ @route_sections ||= itinerary.route_sections.to_a
+ end
+
+ def sections
+ @sections ||= create_sections
+ end
+
+ def create_sections
+ [].tap do |sections|
+ stop_points.each_cons(2).each_with_index do |(departure, arrival), index|
+ sections << Section.new(departure.stop_area, arrival.stop_area, route_sections[index])
+ end
+ end
+ end
+
+ def sections_attributes=(attributes)
+ # Process the attributes hash
+ attributes.each do |index, section_attributes|
+ sections[index.to_i].attributes = section_attributes
+ end
+ end
+
+ def save
+ itinerary.update_attribute :route_section_ids, sections.map(&:route_section_id)
+ end
+
+ class Section
+ extend ActiveModel::Translation
+
+ attr_accessor :departure, :arrival, :route_section_id
+
+ def initialize(departure, arrival, route_section = nil)
+ @departure, @arrival = departure, arrival
+
+ self.route_section = route_section
+ end
+
+ def route_section=(route_section)
+ @route_section = route_section
+ @route_section_id = route_section.respond_to?(:id) ? route_section.id : nil
+ end
+
+ def route_section
+ @route_section ||= candidates.find_by id: route_section_id
+ end
+
+ def persisted?
+ false
+ end
+
+ def candidates
+ @candidates ||= Chouette::RouteSection.where(departure: departure, arrival: arrival)
+ end
+
+ def create_candidate
+ Chouette::RouteSection.create(departure: departure, arrival: arrival)
+ end
+
+ def attributes=(attributes)
+ attributes.each { |k,v| send "#{k}=", v }
+ end
+
+ def valid?
+ route_section.present?
+ end
+
+ end
+
+end
diff --git a/app/views/journey_patterns/show.html.erb b/app/views/journey_patterns/show.html.erb
index dc9e852fa..3a2b18ea1 100644
--- a/app/views/journey_patterns/show.html.erb
+++ b/app/views/journey_patterns/show.html.erb
@@ -34,9 +34,9 @@
<ul class="actions">
<li><%= link_to t('journey_patterns.actions.new'), new_referential_line_route_journey_pattern_path(@referential, @line, @route), :class => "add" %></li>
<li><%= link_to t('journey_patterns.actions.edit'), edit_referential_line_route_journey_pattern_path(@referential, @line, @route, @journey_pattern), :class => "edit" %></li>
- <li><%= link_to t('journey_patterns.actions.destroy'), referential_line_route_journey_pattern_path(@referential, @line, @route, @journey_pattern), :method => :delete, :data => {:confirm => t('journey_patterns.actions.destroy_confirm')}, :class => "remove" %></li>
+ <li><%= link_to t('journey_patterns.actions.destroy'), referential_line_route_journey_pattern_path(@referential, @line, @route, @journey_pattern), :method => :delete, :data => {:confirm => t('journey_patterns.actions.destroy_confirm')}, :class => "remove" %></li>
+ <li><%= link_to t('journey_patterns.actions.edit_route_sections'), edit_referential_line_route_journey_pattern_route_sections_selector_path(@referential, @line, @route, @journey_pattern), class: 'edit' %></li>
<li><%= link_to t('journey_patterns.journey_pattern.vehicle_journey_at_stops'), referential_line_route_vehicle_journeys_path(@referential, @line, @route, :q => {:journey_pattern_id_eq => @journey_pattern.id}), :class => "clock" %></li>
</ul>
<%= creation_tag(@journey_pattern) %>
<% end %>
-
diff --git a/app/views/referentials/_counts.html.erb b/app/views/referentials/_counts.html.erb
index 3ade4e945..20078429b 100644
--- a/app/views/referentials/_counts.html.erb
+++ b/app/views/referentials/_counts.html.erb
@@ -32,6 +32,10 @@
<%= Referential.human_attribute_name("connection_links") %>
</li>
<li class="list-group-item">
+ <span class="badge"><%= @referential.route_sections.size %></span>
+ <%= link_to Referential.human_attribute_name("route_sections"), referential_route_sections_path(@referential) %>
+ </li>
+ <li class="list-group-item">
<span class="badge"><%= @referential.stop_areas.size %></span>
<%= Referential.human_attribute_name("stop_areas") %>
</li>
diff --git a/app/views/route_sections/_form.html.erb b/app/views/route_sections/_form.html.erb
new file mode 100644
index 000000000..28a1dd47a
--- /dev/null
+++ b/app/views/route_sections/_form.html.erb
@@ -0,0 +1,24 @@
+<%= semantic_form_for [@referential, @route_section] do |form| %>
+ <%= form.inputs do %>
+ <% if @route_section.new_record? %>
+ <%= form.input :departure, as: :select, collection: @stop_areas, include_blank: false %>
+ <%= form.input :arrival, as: :select, collection: @stop_areas, include_blank: false %>
+ <% else %>
+ <li class="input optional">
+ <label class="label"><%= @route_section.human_attribute_name("departure") %></label>
+ <%= @route_section.departure.name %>
+ </li>
+ <li class="input optional">
+ <label class="label"><%= @route_section.human_attribute_name("arrival") %></label>
+ <%= @route_section.arrival.name %>
+ </li>
+ <%= form.input :editable_geometry, as: :hidden %>
+ <% end %>
+
+ <%= form.input :no_processing, label: t('.no_processing'), as: :select, collection: { t('.standard_processor') => false, t('.no_processor') => true }, include_blank: false %>
+ <% end %>
+ <%= form.actions do %>
+ <%= form.action :submit, :as => :button, label: t('.submit') %>
+ <%= form.action :cancel, :as => :link %>
+ <% end %>
+<% end %>
diff --git a/app/views/route_sections/edit.html.erb b/app/views/route_sections/edit.html.erb
new file mode 100644
index 000000000..80b7bde4a
--- /dev/null
+++ b/app/views/route_sections/edit.html.erb
@@ -0,0 +1,4 @@
+<%= title_tag t('route_sections.edit.title') %>
+
+<%= @map.to_html %>
+<%= render "form" %>
diff --git a/app/views/route_sections/index.html.erb b/app/views/route_sections/index.html.erb
new file mode 100644
index 000000000..5d7bef989
--- /dev/null
+++ b/app/views/route_sections/index.html.erb
@@ -0,0 +1,56 @@
+<%= title_tag t('.title') %>
+
+<%= form_for search, url: referential_route_sections_path(@referential), method: :get, class: "form-inline" do |form| %>
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <div class="col-md-6">
+ <%= form.text_field :departure_name, class: 'form-control', placeholder: Chouette::RouteSection.human_attribute_name(:departure) %>
+ </div>
+
+ <div class="input-group col-md-6">
+ <%= form.text_field :arrival_name, class: 'form-control', placeholder: Chouette::RouteSection.human_attribute_name(:arrival) %>
+ <div class="input-group-btn">
+ <button class="btn btn-default" type="submit"><i class="fa fa-search"></i></button>
+ </div>
+ </div>
+ </div>
+
+ <div class="panel-body">
+ <div class="col-md-4">
+ <%= form.select :line_id, @referential.lines.order(:name).pluck(:name, :id), { include_blank: t('.all_lines')}, class: 'form-control' %>
+ </div>
+ </div>
+ </div>
+<% end %>
+
+<table class="table table-hover table-striped">
+ <thead>
+ <tr>
+ <th><%= Chouette::RouteSection.human_attribute_name :departure %></th>
+ <th><%= Chouette::RouteSection.human_attribute_name :arrival %></th>
+ <th class="distance"><%= Chouette::RouteSection.human_attribute_name :distance %></th>
+ <th class="points"><%= Chouette::RouteSection.human_attribute_name :points %></th>
+ <th class="actions"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <% @route_sections.each do |route_section| %>
+ <tr>
+ <td><%= route_section.departure.name %> <%= link_to content_tag(:i, "", class: "fa fa-external-link"), referential_stop_area_path(@referential, route_section.departure) %></td>
+ <td><%= route_section.arrival.name %> <%= link_to content_tag(:i, "", class: "fa fa-external-link"), referential_stop_area_path(@referential, route_section.arrival) %></td>
+ <td class="distance"><%= route_section.distance.to_i if route_section.distance %></td>
+ <td class="points"><%= route_section.via_count if route_section.via_count > 0 %></td>
+ <td class="actions">
+ <%= link_to t('.actions.show'), referential_route_section_path(@referential, route_section), class: "view" %>
+ <%= link_to t('.actions.edit'), edit_referential_route_section_path(@referential, route_section), class: "edit" %>
+ <%= link_to t('.actions.destroy'), referential_route_section_path(@referential, route_section), method: :delete, data: { confirm: t('route_sections.actions.destroy_confirm') }, class: "remove" %>
+ </td>
+ </tr>
+ <% end %>
+ </tbody>
+
+</table>
+
+<div class="pagination">
+ <%= will_paginate @route_sections, :container => false, renderer: RemoteBootstrapPaginationLinkRenderer %>
+</div>
diff --git a/app/views/route_sections/new.html.erb b/app/views/route_sections/new.html.erb
new file mode 100644
index 000000000..1ff3c74d0
--- /dev/null
+++ b/app/views/route_sections/new.html.erb
@@ -0,0 +1,3 @@
+<%= title_tag t('route_sections.new.title') %>
+
+<%= render "form" %>
diff --git a/app/views/route_sections/show.html.erb b/app/views/route_sections/show.html.erb
new file mode 100644
index 000000000..61c7c8149
--- /dev/null
+++ b/app/views/route_sections/show.html.erb
@@ -0,0 +1,28 @@
+<%= title_tag t('.title') %>
+
+<div class="route_section">
+ <%= @map.to_html %>
+
+ <div class="summary">
+ <p>
+ <label><%= @route_section.human_attribute_name("departure") %>: </label>
+ <%= @route_section.departure.name %>
+ </p>
+ <p>
+ <label><%= @route_section.human_attribute_name("arrival") %>: </label>
+ <%= @route_section.arrival.name %>
+ </p>
+ <p>
+ <label><%= @route_section.human_attribute_name("distance") %>: </label>
+ <%= "#{@route_section.distance.round}m" if @route_section.distance %>
+ </p>
+ </div>
+</div>
+
+<% content_for :sidebar do %>
+<ul class="actions">
+ <li><%= link_to t('route_sections.actions.edit'), edit_referential_route_section_path(@referential, @route_section), :class => "edit" %></li>
+ <li><%= link_to t('route_sections.actions.destroy'), referential_route_section_path(@referential, @route_section), :method => :delete, :data => {:confirm => t('route_sections.actions.destroy_confirm')}, :class => "remove" %></li>
+</ul>
+ <%= creation_tag(@route_section) %>
+<% end %>
diff --git a/app/views/route_sections/show.kml.erb b/app/views/route_sections/show.kml.erb
new file mode 100644
index 000000000..0d41f0fdb
--- /dev/null
+++ b/app/views/route_sections/show.kml.erb
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kml xmlns="http://www.opengis.net/kml/2.2">
+ <Document>
+ <Placemark id="route-section-<%= @route_section.id %>">
+ <%= @route_section.geometry(params[:mode]).kml_representation.html_safe %>
+ </Placemark>
+ </Document>
+</kml>
diff --git a/app/views/route_sections_selectors/_selection.js.erb b/app/views/route_sections_selectors/_selection.js.erb
new file mode 100644
index 000000000..70d03cf75
--- /dev/null
+++ b/app/views/route_sections_selectors/_selection.js.erb
@@ -0,0 +1,28 @@
+<div>
+ <span class="departure">
+ <label><%= @route_section.human_attribute_name("departure") %>:</label>
+ <%= link_to @route_section.departure.name, referential_stop_area_path(@referential, @route_section.departure) %>
+ </span>
+
+ <span class="arrival">
+ <label><%= @route_section.human_attribute_name("arrival") %>:</label>
+ <%= link_to @route_section.arrival.name, referential_stop_area_path(@referential, @route_section.arrival) %>
+ </span>
+
+ <span class="distance">
+ <label><%= @route_section.human_attribute_name("distance") %>:</label>
+ <%= "#{@route_section.distance.to_i} m" if @route_section.distance > 0 %>
+ </span>
+
+ <% if @route_section.via_count > 0 %>
+ <span class="via_count">
+ <label><%= @route_section.human_attribute_name("via_count") %>:</label>
+ <%= @route_section.via_count if @route_section.via_count > 0 %>
+ </span>
+ <% end %>
+
+ <span class="actions">
+ <%= link_to_edit_route_section @route_section %>
+ <%= link_to_create_route_section @route_section.departure, @route_section.arrival %>
+ </span>
+</div>
diff --git a/app/views/route_sections_selectors/edit.html.erb b/app/views/route_sections_selectors/edit.html.erb
new file mode 100644
index 000000000..0cd58d9c1
--- /dev/null
+++ b/app/views/route_sections_selectors/edit.html.erb
@@ -0,0 +1,48 @@
+<%= title_tag t('.title', journey_pattern: journey_name(@journey_pattern)) %>
+
+<div class = "large-map">
+ <%= @map.to_html %>
+</div>
+
+<%= semantic_form_for resource, url: referential_line_route_journey_pattern_route_sections_selector_path(@referential, @line, @route, @journey_pattern), method: :put do |form| %>
+
+<h3><%= t('.selection.title') %></h3>
+
+<div id="map-selection" style="display: none">
+ <div>
+ </div>
+</div>
+
+<div id="empty-map-selection">
+ <%= t('.no_selection') %>
+</div>
+
+<h3><%= t('.form.title') %></h3>
+
+<table class="table table-hover table-striped">
+ <tbody>
+ <%= form.fields_for :sections do |section_form| %>
+ <tr>
+ <td><%= link_to section_form.object.departure.name, referential_stop_area_path(@referential, section_form.object.departure) %></td>
+ <td class="route_section">
+ <% if section_form.object.candidates.present? %>
+ <%= section_form.input :route_section_id, as: :select, label: false, collection: section_form.object.candidates, include_blank: t('.no_selection') %>
+ <% else %>
+ <%= t('.no_candidate') %>
+ <% end %>
+ </td>
+ <td class="action">
+ <%= link_to_edit_route_section section_form.object.route_section %>
+ <%= link_to_create_route_section section_form.object.departure, section_form.object.arrival %>
+ </td>
+ </tr>
+ <% end %>
+ </tbody>
+</table>
+
+<%= form.actions do %>
+ <%= form.action :submit, as: :button, label: t('.submit') %>
+ <%= form.action :cancel, as: :link, url: referential_line_route_journey_pattern_path(@referential, @line, @route, @journey_pattern) %>
+<% end %>
+
+<% end %>
diff --git a/app/views/shared/_header.erb b/app/views/shared/_header.erb
index 1e8a63fba..9d53d807f 100644
--- a/app/views/shared/_header.erb
+++ b/app/views/shared/_header.erb
@@ -65,6 +65,10 @@
<span class="badge pull-right"><%= @referential.time_tables.size %></span><%= Referential.human_attribute_name("time_tables") %>
<% end %>
</li>
+ <li><%= link_to referential_route_sections_path(@referential) do %>
+ <span class="badge pull-right"><%= @referential.route_sections.size %></span><%= Referential.human_attribute_name("route_sections") %>
+ <% end %>
+ </li>
<li><%= link_to referential_timebands_path(@referential) do %>
<span class="badge pull-right"><%= @referential.timebands.size %></span><%= Referential.human_attribute_name("timebands") %>
<% end %>